什么是SPI
SPI是jdk中的一种服务发现机制,在java中可以用来扩展API和第三方实现。相比于API,可以动态替换发现。
我的理解:SPI是一种动态服务发现方式。如果我们想要调用别人实现的方法,那么肯定是调用别人的实现类来操作。但是java里面是面向接口编程,实现多个类之间的解耦。所以SPI操作就是你可以通过调用接口来调用实现类。那么如何才能知道接口和实现类之间的绑定呢?就是通过配置文件,那么我们怎么加载配置文件,使用Classloader类加载器去加载,这样就可以了。
jdk中使用SPI方式
- 在jar包的META-INF/services目录下创建一个以"接口全限定名"为命名的文件,内容为实现类的全限定名。
- 接口实现类所在的jar包在classpath下。
- 主程序通过java.util.ServiceLoader动态状态实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM。
- SPI的实现类必须带一个无参构造方法。
自定义SPI实现同包调用
思路:
- 创建META-INF/services目录,放在classpath下面(放在resources下面)。
- 在META-INF/services目录下创建一个以"接口全限定名"为命名的文件,内容为实现类的全限定名。
- 主程序通过java.util.ServiceLoader动态状态实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM。
目录结构:
com.sunpy.permissionservice.spi.ISpiSunpyService文件:
com.sunpy.permissionservice.spi.SpiSunpyServiceOne
com.sunpy.permissionservice.spi.SpiSunpyServiceTwo
接口和实现类:
public interface ISpiSunpyService {
public void outMsg();
}
public class SpiSunpyServiceOne implements ISpiSunpyService{
@Override
public void outMsg() {
System.out.println("SpiSunpyServiceOne服务信息");
}
}
public class SpiSunpyServiceTwo implements ISpiSunpyService{
@Override
public void outMsg() {
System.out.println("SpiSunpyServiceTwo服务信息");
}
}
测试:
@Test
public void spiTest() {
ServiceLoader serviceLoader = ServiceLoader.load(ISpiSunpyService.class);
serviceLoader.forEach(ISpiSunpyService::outMsg);
}
自定义SPI实现引入jar包调用
public class SunpyTest {
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/services/com.sunpy.spi.ISpiService";
public static Map> loadClassName() throws IOException {
ClassLoader classLoader = SunpyTest.class.getClassLoader();
Enumeration urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
Map> map = new HashMap<>();
List list = new ArrayList<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
URLConnection urlConnection = url.openConnection();
BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
String content = "";
while ((content = br.readLine()) != null) {
list.add(content);
}
map.put(FACTORIES_RESOURCE_LOCATION, list);
}
return map;
}
public static void doMethod() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Map> map = loadClassName();
List list = map.get(FACTORIES_RESOURCE_LOCATION);
for (String clazzName : list) {
Class> clazz = Class.forName(clazzName);
Method method = clazz.getMethod("outMsg");
method.invoke(clazz.newInstance());
}
}
public static void main(String[] args) throws Exception {
doMethod();
}
}
SPI应用场景
- springboot中的SPI
配置文件:
获取spring中的实例,委派给SpringFactoriesLoader.loadFactoryNames加载全限定类名。
// 获取spring的实例
private Collection getSpringFactoriesInstances(Class type, Class>[] parameterTypes, Object... args) {
// 获取类加载器
ClassLoader classLoader = getClassLoader();
/**
* 使用给定的类加载器从“META-INF/spring.factories”加载给定类型的工厂实现的全限定类名。
*/
Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 创建实例集合
List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
SpringFactoriesLoader.loadFactoryNames的实现
private static Map> loadSpringFactories(ClassLoader classLoader) {
Map> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
// public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
Enumeration urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// Replace all lists with unmodifiable lists containing unique elements
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
查看:
实例化类:
@SuppressWarnings("unchecked")
private List createSpringFactoriesInstances(Class type, Class>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set names) {
List instances = new ArrayList<>(names.size());
// 拿着加载的所有的全限定名,进行实例化
for (String name : names) {
try {
// 反射加载全限定名的Class
Class> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
// 反射获取Constructor类
Constructor> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
// 反射通过构造器实例化类
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
参考
https://xie.infoq.cn/article/2021f8d92c93c94b96ebf4a3d
https://www.cnblogs.com/xrq730/p/11440174.html
https://blog.csdn.net/weixin_43476824/article/details/125739140