我是靠谱客的博主 外向星月,最近开发中收集的这篇文章主要介绍spring SPI SpringFactoriesLoader概念,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

概念

SPI(service provider interface)是JDK内置的中服务发现机制,和JNDI的作用一样,只是使用方式不一样。都可以用约定好的标识,去获相关服务的实现,区别是SPI通过接口类型去查询,JNDI通过服务的命名去查询,用法如下:

  1. 定义服务的接口
package com.test
public interface ServiceInterface {
    String service();
}
  1. 实现接口
package com.test
public ServiceImp implements ServiceInterface {
    String service() {
        return "";
    }
}
  1. 编写配置文件,文件路径必须为META-INF/service/com.test.ServiceInterface (接口名),配置文件中放实现类(com.test.ServiceImp)

  2. 编码获取service

 public static void main(String[] args) {
    ServiceLoader<ServiceInterface > services = ServiceLoader.load(ServiceInterface .class);
    for (ServiceInterface service: services ){
        System.out.println(service.service());
    }
 }

SPI的用途

SPI一般用于平台类软件,来实现插件的效果。当平台类软件运行时,有些功能必须由外部软件来实现,这样平台类可以定义好接口,外部软件按SPI规范实现好接口,并放入到类路径中,平台内软件运行时,就可以通过ServiceLoader获取到具体的实现,来实现可插拔,松耦合的效果。
比如,之前分析的spring通过实现ServletContainerInitializer接口,来实现servlet的配置。

spring SPI

spring也实现了自己的SPI功能,可能考虑到以下两点吧:

  1. SPI的配置文件的路径是约定好的,如果复用jdk的SPI机制,配置文件可能产生冲突,导致运行异常。
  2. spring想尽量小的依赖JDK

差异点

  1. 配置文件不一样,spring的配置文件路径为META-INF/spring.factories,且文件内容为键值对(接口=实现类)
  2. loader不一样JDK的是ServiceLoader,spring的是SpringFactoriesLoader(org.springframework.core.io.support)

SpringFactoriesLoader源码分析

service的加载过程其实比较简单,因为JDK中classLoader.getResources的方法可以方便的获取到类路径上的文件。
加载过程:

  1. 获取所有spring.factories文件的URL
  2. 通过URL获取输入流,并将文件内容存储到Properties类中
  3. 合并数据
// 代码有删减
public final class SpringFactoriesLoader {
    // spring SPI配置文件的路径
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    // 同一个接口可能有多个实现类,所以用MultiValueMap存储,一个key值可以对应多个value
	private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
	// 获取所有的SPI
	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
	    // classLoader.getResources方法可以方便的获取到类路径上面的文件
		Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
		result = new LinkedMultiValueMap<>();
		while (urls.hasMoreElements()) {
			URL url = urls.nextElement();
			UrlResource resource = new UrlResource(url);
			// 使用Properties.load方法,去加载文件中的配置
			Properties properties = PropertiesLoaderUtils.loadProperties(resource);
			for (Map.Entry<?, ?> entry : properties.entrySet()) {
				String factoryTypeName = ((String) entry.getKey()).trim();
				// 配置文件中同一个接口的实现类,可以配置多个,中间使用逗号或分号分隔
				for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
					result.add(factoryTypeName, factoryImplementationName.trim());
				}
			}
		}
		return result;
	}
     // 其他方法为查询方法,从map中查询某个接口的实现类,没啥好说的
}

最后

以上就是外向星月为你收集整理的spring SPI SpringFactoriesLoader概念的全部内容,希望文章能够帮你解决spring SPI SpringFactoriesLoader概念所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(34)

评论列表共有 0 条评论

立即
投稿
返回
顶部