最近有个项目需要存储文件内容,客户又不使用产品默认对象存储,产品又不想将定制的代码融入到产品中,因此需要设计成可插拔的插件,初次接触到SPI 和 SpringBoot Starter。两者很像,又有所区别,这里梳理一下SPI实现;下面是一个完整的示例;
网上很多博客所有代码都揉在一个工程中,不便于理解和使用,这里按真正的使用场景分为了3个工程;示例需要3个jar ; 接口jar 、插件jar、 业务jar;文章最后有源码链接。
IDEA工程如下:
1.编写接口jar--供另外两个jar使用
pom坐标
<?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.idto.spi</groupId>
<artifactId>idto-spi-api</artifactId>
<version>1.0</version>
<description>SPI-接口层jar包</description>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>7</source>
<target>7</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
</dependencies>
</project>
定义一个接口类Say
package com.idto.spi;
/**
* @author idto
* @title: Say
* @projectName
* @description: SPI定义的接口 需要业务层和实现的插件引用
* @date 2022/2/22 10:40
*/
public interface Say {
void sayWithLanguage(String language);
}
将idto-spi-api.jar编译打包到本地仓库中,稍后使用。
2.编写SPI插件--插件jar
pom坐标,引入第1步打好的idto-spi-api.jar包
<?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>cn.org.bjca</groupId>
<artifactId>idto-spi-plug</artifactId>
<version>1.0</version>
<description>SPI-插件jar包</description>
<dependencies>
<!--引入定义的接口-->
<dependency>
<groupId>com.idto.spi</groupId>
<artifactId>idto-spi-api</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>7</source>
<target>7</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
编写接口Say的实现类,这里编写了两个实现类
package com.idto.spi.impl;
import com.idto.spi.Say;
public class America implements Say {
@Override
public void sayWithLanguage(String s) {
System.out.println("Speak English -->"+s);
}
}
package com.idto.spi.impl;
import com.idto.spi.Say;
public class China implements Say {
@Override
public void sayWithLanguage(String s) {
System.out.println("中文说-->"+s);
}
}
划重点
在resources下新建META-INF.services ,然后在services下新建文件com.idto.spi.Say(接口的全路径),com.idto.spi.Say在文件中写入要用到的实现类的全路径,多个实现类分行写入
将idto-spi-plug.jar编译打包到本地仓库中,稍后使用。
到这不SPI实现已经完成,下面我们看看怎么使用吧!
3.编写业务代码--业务jar
pom坐标,引入第1步打好的idto-spi-api.jar包
<?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>cn.org.bjca</groupId>
<artifactId>idto-spi-business</artifactId>
<version>1.0</version>
<description>SPI-业务jar包</description>
<dependencies>
<!--引入定义的接口-->
<dependency>
<groupId>com.idto.spi</groupId>
<artifactId>idto-spi-api</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resource</directory>
<includes>
<include>*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
<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.idto.spi.Main</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<!-- 打包时过滤无用的文件,不打包进jar -->
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
<filter>
<artifact>junit:junit</artifact>
<includes>
<include>junit/framework/**</include>
<include>org/junit/**</include>
</includes>
<excludes>
<exclude>org/junit/experimental/**</exclude>
<exclude>org/junit/runners/**</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
编写SPI的调用方法,通过java自带的ServiceLoader加载插件;代码中指定插件的目录,最好在项目jar外,方便插拔,代码中写死了插件的名称,仅供参考
package com.idto.spi;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ServiceLoader;
/**
* @author idto
* @title: Main
* @projectName idto-spi-business
* @description: SPI调用例子
* @date 2022/2/22 13:02
*/
public class Main {
public static void main(String[] args) throws MalformedURLException {
// 获取插件的路径
String projectPath = System.getProperty("user.dir");
Path path = Paths.get(projectPath);
String plugPath = path.toString() + File.separator + "plugs";
String location = plugPath + File.separator + "idto-spi-plug-1.0.jar";
System.out.println("plug Path is -->" + location);
System.out.println( );
System.out.println( );
System.out.println("---start---");
URL url = new File(location).toURI().toURL();
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url}, ClassLoader.getSystemClassLoader());
ServiceLoader<Say> says = ServiceLoader.load(Say.class, urlClassLoader);
for (Say say : says) {
say.sayWithLanguage("Hello World!");
}
System.out.println("---end---");
}
}
到这步,可以直接在代码进行测试了;
为了模拟真是使用场景,我们打包编译,将打好的idto-spi-business-1.0.jar 复制到一个测试目录下进行测试;
测试场景一:不放插件
没有找到对应的实现类,打印为空
测试场景一:按照指定目录安放插件
在D:\log\plugs下放入idto-spi-plug-1.0.jar
运行业务类代码,结果两个实现类的方法成功运行,如下:
原创不易,如对您有所帮助,欢迎您点赞 收藏(#^.^#)