Spring源码之SpringFactoriesLoader(一)
作用
SpringFactoriesLoader可以加载jar包下META-INF下的spring.factories,把相关接口的实现按照key,value的形式加载到内存,一个接口的多个实现可以按照","进行分割。对程序员来说,利用SpringFactoriesLoader可以加载自定义的ApplicationListener、ApplicationContextInitializer、Configuration类等,spring-boot的starter就是通过SpringFactoriesLoader加载了相关Configuration配置类。 以下是spring的官方解释:
源码
我们可以通过SpringFactoriesLoader加载ApplicationContextInitializer的流程分析SpringFactoriesLoader的源码。 1、ApplicationContextInitializer接口相关类的加载是在SpringApplication构造方案里面完成的。 2、进入getSpringFactoriesInstances方法内,可以看到调用了SpringFactoriesLoader.loadFactoryNames(type, classLoader),其中传入了ClassLoader,说明每个同一个类被不同的ClassLoader加载在JVM中属于不同的类。 3、进入loadFactoryNames,调用了loadSpringFactories方法。 4、进入loadSpringFactories方法中。
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 根据classLoader从缓存取,MultiValueMap是一个key,values的数据结构,一个key对应多个value
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// 扫描所有jar下面的META-INF/spring.factories
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 把spring.factories数据加载到Properties
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
// 遍历所有的行数据
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
// 把value按照,分割成数组
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
// 加入缓存
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
示例
1、在resource下面创建META-INF/spring.factories文件,加入
org.springframework.context.ApplicationContextInitializer=com.zjl.spring.initializer.FirstInitializer org.springframework.context.ApplicationListener=com.zjl.spring.listener.FirsetListener,com.zjl.spring.listener.FourthListener
2、debug查看数据已经被加载
starter中的示例
mybatis-spring-boot-starter中引入了org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration 配置类,从而帮助自动注入一些DataSource、SqlSessionFactory等类。
总结
SpringFactoriesLoader是Spring的一个内部加载工厂类,我们自定义的一些ApplicationListener、ApplicationContextInitializer可以通过它加载到内存中,同时starter也必须通过SpringFactoriesLoader把Configuration类加载到内存中,从而被spring解析。
