不灭的焱

革命尚未成功,同志仍须努力

作者:php-note.com  发布于:2018-10-23 00:36  分类:Java基础 

SPI简介

这里先说下SPI的一个概念,SPI英文为Service Provider Interface单从字面可以理解为Service提供者接口,正如从SPI的名字去理解SPI就是Service提供者接口;我对SPI的定义:提供给服务提供厂商与扩展框架功能的开发者使用的接口。

在我们日常开发的时候都是对问题进行抽象成Api然后就提供各种Api的实现,这些Api的实现都是封装与我们的Jar中或框架中的虽然当我们想要提供一种Api新实现时可以不修改原来代码只需实现该Api就可以提供Api的新实现,但我们还是生成新Jar或框架(虽然可以通过在代码里扫描某个目录已加载Api的新实现,但这不是Java的机制,只是hack方法),而通过Java SPI机制我们就可以在不修改Jar包或框架的时候为Api提供新实现。

很多框架都使用了java的SPI机制,如java.sql.Driver的SPI实现(MySQL驱动、oracle驱动等)、common-logging的日志接口实现、dubbo的扩展实现等等框架;

SPI约定

1) 在 META-INF/services/ 目录中创建以接口全限定名命名的文件,该文件内容为Api具体实现类的全限定名;

2) 使用 ServiceLoader类 动态加载 META-INF 中的实现类;

3) 如SPI的实现类为Jar,则需要放在主程序classPath中;

4) Api具体实现类必须有一个不带参数的构造方法;

简单示例

通过一个简单例子来说明SPI是如何使用的。 首先通过一张图来看看,用SPI需要遵循哪些规范,因为spi毕竟是JDK的一种标准。 

"" 

完整的Maven示例

1、目录结构

[spi-demo]
├── pom.xml
├── spi-demo-lib
│   ├── pom.xml
│   ├── src
│   │   ├── main
│   │   │   ├── java
│   │   │   │   └── com
│   │   │   │       └── jianbao
│   │   │   │           └── IService.java
│   │   │   └── resources
│   │   └── test
│   │       └── java
│   └── target
├── spi-demo-main
│   ├── pom.xml
│   ├── src
│   │   ├── main
│   │   │   ├── java
│   │   │   │   └── com
│   │   │   │       └── jianbao
│   │   │   │           └── main
│   │   │   │               └── Main.java
│   │   │   └── resources
│   │   └── test
│   │       └── java
│   └── target
├── spi-demo-service1
│   ├── pom.xml
│   ├── src
│   │   ├── main
│   │   │   ├── java
│   │   │   │   └── com
│   │   │   │       └── jianbao
│   │   │   │           └── services
│   │   │   │               └── impl
│   │   │   │                   ├── Service1.java
│   │   │   │                   └── Service2.java
│   │   │   └── resources
│   │   │       └── META-INF
│   │   │           └── services
│   │   │               └── com.jianbao.IService
│   │   └── test
│   │       └── java
│   └── target
└── spi-demo-service3
    ├── pom.xml
    ├── src
    │   ├── main
    │   │   ├── java
    │   │   │   └── com
    │   │   │       └── jianbao
    │   │   │           └── services
    │   │   │               └── impl
    │   │   │                   └── Service3.java
    │   │   └── resources
    │   │       └── META-INF
    │   │           └── services
    │   │               └── com.jianbao.IService
    │   └── test
    │       └── java
    └── target

2、项目文件内容

(1)模块 spi-demo

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.jianbao</groupId>
    <artifactId>spi-demo</artifactId>
    <packaging>pom</packaging>
    <version>1.0.0</version>
    <modules>
        <module>spi-demo-service1</module>
        <module>spi-demo-service3</module>
        <module>spi-demo-lib</module>
        <module>spi-demo-main</module>
    </modules>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.jianbao</groupId>
                <artifactId>spi-demo-lib</artifactId>
                <version>1.0.0</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

 

(2) 模块 /spi-demo/spi-demo-lib

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.jianbao</groupId>
    <artifactId>spi-demo-lib</artifactId>
    <version>1.0.0</version>

</project>

IService.java

package com.jianbao;

public interface IService {
    void Foo();
}

 

(3) 模块 /spi-demo/spi-demo-service1

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.jianbao</groupId>
        <artifactId>spi-demo</artifactId>
        <version>1.0.0</version>
    </parent>
    
    <artifactId>spi-demo-service1</artifactId>
    <version>1.0.0</version>
    
    <dependencies>
        <dependency>
            <groupId>com.jianbao</groupId>
            <artifactId>spi-demo-lib</artifactId>
        </dependency>
    </dependencies>
    
</project>

Service1.java

package com.jianbao.services.impl;

import com.jianbao.IService;

public class Service1  implements IService {
    @Override
    public void Foo() {
        System.out.println("service1 on called");
    }
}

Service2.java

package com.jianbao.services.impl;

import com.jianbao.IService;

public class Service2 implements IService {
    @Override
    public void Foo() {
        System.out.println("service2 on called");
    }
}

META-INF/services/com.jianbao.IService

com.jianbao.services.impl.Service1
com.jianbao.services.impl.Service2

 

(4) 模块 /spi-demo/spi-demo-service3

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <parent>
        <groupId>com.jianbao</groupId>
        <artifactId>spi-demo</artifactId>
        <version>1.0.0</version>
    </parent>
    
    <artifactId>spi-demo-service3</artifactId>
    <version>1.0.0</version>

    <dependencies>
        <dependency>
            <groupId>com.jianbao</groupId>
            <artifactId>spi-demo-lib</artifactId>
        </dependency>
    </dependencies>
</project>

Service3.java

package com.jianbao.services.impl;

import com.jianbao.IService;

public class Service3 implements IService {
    @Override
    public void Foo() {
        System.out.println("service3 on called");
    }
}

META-INF/services/com.jianbao.IService

com.jianbao.services.impl.Service3

 

(5) 模块 /spi-demo/spi-demo-main

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.jianbao</groupId>
        <artifactId>spi-demo</artifactId>
        <version>1.0.0</version>
    </parent>

    <artifactId>spi-demo-main</artifactId>
    <version>1.0.0</version>

    <dependencies>
        <dependency>
            <groupId>com.jianbao</groupId>
            <artifactId>spi-demo-lib</artifactId>
        </dependency>
        
        
        <dependency>
            <groupId>com.jianbao</groupId>
            <artifactId>spi-demo-service1</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.jianbao</groupId>
            <artifactId>spi-demo-service3</artifactId>
            <version>1.0.0</version>
        </dependency>



    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.1.1</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.jianbao.main.Main</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

Main.java

package com.jianbao.main;

import com.jianbao.IService;

import java.util.ServiceLoader;

public class Main {
    public static void main(String[] args) throws Exception {
        // 【推荐】输出 方式一
        ServiceLoader<IService> services = ServiceLoader.load(IService.class);
        for (IService service : services) {
            service.Foo();
        }

        // 输出 方式二        
//        ServiceLoader<IService> serviceList = ServiceLoader.load(IService.class);
//        Iterator<IService> serviceIterator = serviceList.iterator();
//        //System.out.println("classPath:" + System.getProperty("java.class.path"));
//        while (serviceIterator.hasNext()) {
//            IService service = serviceIterator.next();
//            service.Foo();
//        }
    }
}

3、执行代码:

(1) 切换到 项目根目录下,执行

mvn clean install

(2) 运行 main 方法所在的 jar 文件

java -jar ./spi-demo-main/target/spi-demo-main-1.0.0.jar 

输出:

service1 on called
service2 on called

当把 pom.xml 中对 spi-demo-service1 模块的引用去掉后,重新编译,输出:

service3 on called

4、得出结论(特别注意的地方)

当有多个 jar 文件同时实现该接口,并同时被项目引用的话,只有第一个 被引用的  jar 文件起作用!

 

 

参考:

Java的SPI机制浅析与简单示例

Java中的SPI机制

Java SPI机制(动态接口实现)