盘点 SpringBoot : Factories 处理流程

总文档 :文章目录
Github : github.com/black-ant

一 . 前言

之前说了 SpringBoot 自动配置的地方 , 其中就涉及到 Factories 的加载 , 这一篇详细的把这个流程过一下.

Factories 的作用是工厂类的元数据 , Spring 会通过 .factories 文件将需要初始化的类反射出来

二 . 核心类

Factories 流程中核心的类是 SpringFactoriesLoader , 在 SpringApplication run 时 , 即会加载 Factories相关属性

  • spring.factories 配置文件 : Spring 自己的一套 SPI 机制的配置文件
  • SpringFactoriesLoader 类,用于加载 spring.factories 配置文件

先看一下 SpringFactoriesLoader 主要流程

SpringFactoriesLoader
	SF- FACTORIES_RESOURCE_LOCATION : 
	SF- Map<ClassLoader,MultivalueMap> cache
	M- loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader)
		|- 获得接口的类名 ->  factoryClass.getName()
		|- 加载 FACTORIES_RESOURCE_LOCATION 配置文件,获得接口对应的实现类名们
			|-> loadSpringFactories
	M- loadSpringFactories(@Nullable ClassLoader classLoader)
		|- 缓存已存在 , 则直接返回
		|- 获得 FACTORIES_RESOURCE_LOCATION 对应的 URL 们
		|- 创建 LinkedMultiValueMap<String, String> 对象 = result 
		|- 遍历 URL 数组
		|- 获得 URL = url
		|- 创建 UrlResource 对象(url)
		|- 加载 "META-INF/spring.factories" 配置文件,成为 Properties 对象
		|- 遍历 Properties 对象
		|- 使用逗号分隔
		|- 添加到 result 中
		|- 加到 cache 中
	M- loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader)
		?- 获得接口对应的实现类名们,然后创建对应的对象们
		|- 获得 ClassLoader -> SpringFactoriesLoader.class.getClassLoader()
		|- 获得接口对应的实现类名们 -> loadFactoryNames
		|- 遍历 factoryNames 数组,创建实现类的对象 -> for + result.add
		|- 排序 -> AnnotationAwareOrderComparator.sort(result);
        M- instantiateFactory
            |- 获得 Class 类
            |- 判断是否实现了指定接口
            |- 创建对象

复制代码

三 . 流程

3.1 起点

factories 加载的起点就在 SpringApplication 中.

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //获取  ApplicationContextInitializer 的 Factories
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //获取  ApplicationListener 的 Factories
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

复制代码

3.2 Application 的 Factories 加载流程


C1- SpringApplication
    M1_01- getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args)
        ?- 获取 SpringFactories 的实例
        - 获取一个 ClassLoader , 这里就是常规的 AppClassLoader
        - 通过 SpringFactoriesLoader 加载 FactoryNames
        - createSpringFactoriesInstances 创建实例 -> M1_02
        - 排序后返回
    M1_02- createSpringFactoriesInstances
        -  ClassUtils.forName 获取反射类 , BeanUtils.instantiateClass 创建实例
    
        
C2- SpringFactoriesLoader
    F01-  Map<ClassLoader, MultiValueMap<String, String>> cache
    M2_01- loadFactoryNames
        - factoryClass.getName() : 获得接口的类名 -> PS_M2_01_1
	- loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList())
            ?- 加载 FACTORIES_RESOURCE_LOCATION 配置文件,获得接口对应的实现类名们
    M2_02- loadSpringFactories(@Nullable ClassLoader classLoader)
        ?- 加载 FACTORIES_RESOURCE_LOCATION 配置文件,获得接口对应的实现类名们
        - 首先从 cache (F01)中获取 MultiValueMap , 存在直接返回
        - 如果 ClassLoader 存在 , classLoader.getResources(FACTORIES_RESOURCE_LOCATION) 
        - 如果 ClassLoader 不存在 , ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)
            ?- PS_M2_02_1 , 获取文件资源迭代器
        - 迭代加载 , 先获取 UrlResource , 再获取 Properties 
        FOR- 迭代 Properties 属性 , 添加到 Map 中 ,放入缓存
            ?- PS_M2_02_2 添加详情
		
                
// PS_M2_01_1 : 此处参数为 org.springframework.context.ApplicationContextInitializer
// PS_M2_02_1 : location 
    - FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
    - location 不止一个包中有 , 每个包中都可以有 spring.factories , 他们会循环迭代


// M2_02 伪代码
Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader){
    MultiValueMap<String, String> result = cache.get(classLoader);
    if (result != null) {
        return result;
    }
    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);
        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());
            }
        }
    }
    cache.put(classLoader, result);
    return result;
}          

复制代码

PS_M2_02_2 详情:

result.add(factoryTypeName, factoryImplementationName.trim())

image.png

spring.factories 对应结构

org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
复制代码

可以看到 , factoryTypeName 是第一个节点 , 地下为对应的实现

getResources 和 getSystemResources 的区别

3.3 Environment 流程 加载 loadFactories

加载的起点

加载 Environment 时加载的起点是 SpringApplication , 他的加载主要来自于 SpringListener

C- SpringApplication
    M- run(String... args)
        - prepareEnvironment(listeners, applicationArguments)

// 再次调用 : listener.environmentPrepared(environment)


C3- ConfigFileApplicationListener
    M3_01-  loadPostProcessors() : 加载

// M3_01 伪代码
return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());



        
复制代码

加载流程

专门截取出来主要是因为这里调用的方法和上一个是有区别的

loadFactories 除了获取上文的集合 ,同时会通过 instantiateFactory 加载实例 , 他从“META-INF/spring”中加载并实例化给定类型的工厂实现.


C2- SpringFactoriesLoader
    M2_03- loadFactories


复制代码
P- FACTORIES_RESOURCE_LOCATION : 
	-> 静态属性,定义了读取的是 "META-INF/spring.factories" 配置文件
	
P- Map<ClassLoader, MultiValueMap<String, String>> cache 
	-> 读取 "META-INF/spring.factories" 配置文件的缓存
	
M- loadFactories : 获得接口对应的实现类名们,然后创建对应的对象们
	-> classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
	-> loadFactoryNames(factoryClass, classLoaderToUse);
		?- 获得接口对应的实现类名们
	-> new ArrayList<>(factoryNames.size());
	FOR->  遍历 factoryNames 数组,创建实现类的对象
		-> result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
	-> AnnotationAwareOrderComparator.sort(result); -- 排序
	

	

M- instantiateFactory
	-> ClassUtils.forName(instanceClassName, classLoader);
		?- 获得 Class 类
	->  if (!factoryClass.isAssignableFrom(instanceClass)) {
    	?- 判断是否实现了指定接口
    	-> throw new IllegalArgumentException
    -> 	ReflectionUtils.accessibleConstructor(instanceClass).newInstance();
    	-> 创建对象
            


复制代码

其他主要使用 factories 的位置


// 加载 EnvironmentPostProcessor 的 Factories
C- ConfigFileApplicationListener
    M- loadPostProcessors
        - SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader())
        
C- ConfigFileApplicationListener
    - SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,getClass().getClassLoader());
   
   
C- AutoConfigurationImportSelector
    - SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);                                
        

复制代码

附录: 2者对比

PS : 看了一下好像没什么区别 , 方式一创建的大多数是 ApplicationContextInitializer 和 ApplicationListener 的对象 , 从而被设置到 SpringApplication 中

image.png

3.4 factories 文件的扫描

核心逻辑在 loadSpringFactories 方法中


C2- SpringFactoriesLoader
    M2_02- loadSpringFactories(@Nullable ClassLoader classLoader)
        - PropertiesLoaderUtils.loadProperties
        
C- PropertiesLoaderUtils
    M- loadProperties : 调用 fillProperties    
    M- fillProperties : 后面就是读成一个 inputStream 了 , 没有看的价值
         - String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
         - String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
         - put(key, value);
         
// PS : Properties extends Hashtable<Object,Object> 
         
复制代码

3.5 使用技巧

TODO : 今天不想写了 , 看心情补

四 . 总结

基础 : SPI 机制 + Properties 扫描能力
流程 : 启动时扫描ApplicationContextInitializer + ApplicationListener , 运行时扫描其他相关Factories , 并且实例化

总结 : Factories 比较简单 ,到最后也没看懂为什么会有2种不同的加载途径 , 其底层都是使用 构造器 + instance 实现一个对象 , 猜测可能和加载顺序优化

PS : 有清楚的欢迎在评论告知一下 感谢❤❤❤

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享