目录

spring.factories加载原理

1. SpringApplication的构造方法

1.1 SpringApplication#getSpringFactoriesInstances

1.1.1SpringFactoriesLoader#loadFactoryNames-->loadSpringFactories

1.1.2 createSpringFactoriesInstances

自定义EnvironmentPostProcessor

疑问:如何让自定义的EnvironmentPostProcessor的生效呢???

原因介绍

解决办法


spring.factories加载原理

1. SpringApplication的构造方法

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));this.webApplicationType = WebApplicationType.deduceFromClasspath();//用于获取Spring中ApplicationContextInitializer接口的指定类实例用的;//并且获取的时候是根据读取整个项目中文件路径为META-INF/spring.factories 中的内容实例化对应的实例类的,这一步之前SpringFactoriesLoader中的属性cache为nullsetInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class));
//用于获取Spring中ApplicationListener接口的指定类实例用的;//并且获取的时候是根据读取整个项目中文件路径为META-INF/spring.factories 中的内容实例化对应的实例类的,今天着重介绍该方法
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();}

1.1 SpringApplication#getSpringFactoriesInstances

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {//返回指定类型(ApplicationListener)的实例return getSpringFactoriesInstances(type, new Class<?>[] {});}//真正执行的方法
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {//得到类加载器ClassLoader classLoader = getClassLoader();// 得到对应的bean的名字Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));//利用反射生成实例化对象List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);AnnotationAwareOrderComparator.sort(instances);//添加到启动的listeners中return instances;}

1.1.1SpringFactoriesLoader#loadFactoryNames-->loadSpringFactories

//ConcurrentMap(线程安全的map集合)作为缓存cache、通过构造器去创建实例(accessibleConstructor)、isAssignableFrom判断是否是其子类(isAssignableFrom和instanceof关键字的区别)/*** 通用工厂加载机制实现的内部使用框架。spring内部使用的** springfactoresloader会加载META-INF/spring.factories文件中给定类型的工厂,* 为什么是工厂,因为一个key可以对应多个value,* spring.factories的文件类型是properties格式的,其中键是完全限定的接口或抽象类的名称,值是以逗号分隔的** 比如:* <pre class="code">example.MyService=example.MyServiceImpl1,example.MyServiceImpl2</pre>**/
public final class SpringFactoriesLoader {/*** 本地的工厂文件,* 可以存在于多个jar中,也就是他会扫码所有的jar,然后解析所有的META-INF/spring.factories文件,* 并将其配置的创建其工厂,以及对所有value进行实例化*/public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";// 日志框架private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);// 定义一个map,其中key为ClassLoader,value为MultiValueMap// MultiValueMap集成自Map// ConcurrentReferenceHashMap集成自ConcurrentMap,也就是一个线程安全的mapprivate static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();// 空参构造函数private SpringFactoriesLoader() {}/*** 使用给定的类加载器从META-INF/spring.factories加载给定类型的工厂实现的完全限定类名。* @param factoryType 接口或者抽象类的Class对象** @param classLoader classLoader 用于加载的类加载器(可以是null,如果是null,则使用默认值)** @throws IllegalArgumentException 如果在加载工厂名称时发生错误* @see #loadFactories*/public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {// 通过Class对象获取全限定类名(org.springframework.context.ApplicationListener)String factoryTypeName = factoryType.getName();// loadSpringFactories方法是获取所有的springFactories// getOrDefault是通过key即权限定名称,获取到对应的类的集合。因为value是通过逗号相隔的,可以有多个,所以是list// getOrDefault如果存在就返回,如果不存在那么就返回给的默认值return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());}// 加载所有的springFactoriesprivate static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {// 首先从cache中获取,根据classLoader,// cache是以ClassLoader作为key的。是静态的final修饰的。整个应用只有一份MultiValueMap<String, String> result = cache.get(classLoader);// 如果为null,证明还没有加载过,如果不为空,那么就添加。if (result != null) {return result;}try {// 三目表达式,判断参数classLoader是否为空,如果不为空,那么直接使用传入的classLoader获取META-INF/spring.factories// 如果为空,那么就使用系统的classLoader来获取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()) {// 通过循环遍历所有的META-INF/spring.factoriesURL url = urls.nextElement();UrlResource resource = new UrlResource(url);// 解析propertiesProperties properties = PropertiesLoaderUtils.loadProperties(resource);// 将所有的key放入resultfor (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryTypeName = ((String) entry.getKey()).trim();for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {//factoryTypeName//org.springframework.boot.autoconfigure.EnableAutoConfiguration                         //factoryImplementationName.trim()//  com.nieyp.ausware.elasticsearch.ElasticsearchStarterAutoConfigurationresult.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);}}/*** 通过classLoader从各个jar包的classpath下面的META-INF/spring.factories加载并解析其key-value值,然后创建其给定类型的工厂实现** 返回的工厂通过AnnotationAwareOrderComparator进行排序过的。* AnnotationAwareOrderComparator就是通过@Order注解上面的值进行排序的,值越高,则排的越靠后** 如果需要自定义实例化策略,请使用loadFactoryNames方法获取所有注册工厂名称。** @param  factoryType 接口或者抽象类的Class对象* @param  classLoader 用于加载的类加载器(可以是null,如果是null,则使用默认值)* @throws IllegalArgumentException 如果无法加载任何工厂实现类,或者在实例化任何工厂时发生错误,则会抛出IllegalArgumentException异常*/public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {// 首先断言,传入的接口或者抽象类的Class对象不能为空Assert.notNull(factoryType, "'factoryType' must not be null");// 将传入的classLoader赋值给classLoaderToUse// 判断classLoaderToUse是否为空,如果为空,则使用默认的SpringFactoriesLoader的classLoaderClassLoader classLoaderToUse = classLoader;if (classLoaderToUse == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}// 加载所有的META-INF/spring.factories并解析,获取其配置的factoryNames//factoryType为EnvironmentPostProcessor.classList<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);if (logger.isTraceEnabled()) {logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);}List<T> result = new ArrayList<>(factoryImplementationNames.size());// 通过反射对所有的配置进行实例化。for (String factoryImplementationName : factoryImplementationNames) {result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));}AnnotationAwareOrderComparator.sort(result);return result;}// 实例化工厂,根据@SuppressWarnings("unchecked")private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {try {// 根据全限定类名通过反射获取Class对象Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);// 判断获取的Class对象是否从factoryType里面来,// 说具体点就是判断我们配置的spring.factories中的权限定类名所对应的类是否是对应的子类或者实现类// 比如// org.springframework.context.ApplicationContextInitializer=\// org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\// org.springframework.boot.context.ContextIdApplicationContextInitializer,// 那么他就会验证ConfigurationWarningsApplicationContextInitializer和ContextIdApplicationContextInitializer是否是ApplicationContextInitializer的子类
//          isAssignableFrom()方法与instanceof关键字的区别总结为以下两个点:
//          isAssignableFrom()方法是从类继承的角度去判断,instanceof关键字是从实例继承的角度去判断。
//          isAssignableFrom()方法是判断是否为某个类的父类,instanceof关键字是判断是否某个类的子类。// 如果不是,则会保存//factoryType:interface org.springframework.boot.env.EnvironmentPostProcessor//factoryImplementationClass:class com.nieyp.ausware.elasticsearch.core.CustormEnvironmentPostProcessor//需要值得注意的是isAssignableFrom被native修饰,不能直接看实现//public native boolean isAssignableFrom(Class<?> cls);if (!factoryType.isAssignableFrom(factoryImplementationClass)) {throw new IllegalArgumentException("Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");}// 通过反射的有参构造函数进行实例化:如果直接newInstance的话,那么只能通过空参构造函数进行实例化。// 通过这种方式可以通过不同参数的构造函数进行创建实例,但是这里并没有传入参数,所以调用的是默认空惨构造函数return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();}catch (Throwable ex) {throw new IllegalArgumentException("Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]",ex);}}}
  • 走完loadSpringFactories方法的返回值:会读取所有的spring.factories并将对应的key和balue放到cache中,下次可以直接从cache中取(setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class))该方法就是直接从cache中去的,以及后面的获取所有的environmentPostProcessor的实现),注意此时并没有生成实例.

  • 走完loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()):会根据传入的factoryTypeName过滤出需要的beanName

1.1.2 createSpringFactoriesInstances

  • 通过反射实例化(这里不再介绍)
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,ClassLoader classLoader, Object[] args, Set<String> names) {List<T> instances = new ArrayList<>(names.size());for (String name : names) {try {Class<?> instanceClass = ClassUtils.forName(name, classLoader);Assert.isAssignable(type, instanceClass);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;}
  • 此时就完成了我们的spring.factories加载的过程.当然不仅仅只有此处加载;在AutoConfigurationImportSelector,ConfigFileApplicationListener(下面会介绍)也会有相关调用

自定义EnvironmentPostProcessor

public class CustormEnvironmentPostProcessor implements EnvironmentPostProcessor{@Overridepublic void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {//得到的是PropertySource集合,当前也可以通过指定具体的name的带一个具体的PropertySourcePropertySource<?> configurationProperties = environment.getPropertySources().get("configurationProperties");MutablePropertySources propertySources = environment.getPropertySources();for (PropertySource<?> propertySource : propertySources) {Object object = propertySource.getProperty("com.name");if (ObjectUtils.isNotEmpty(object) || object instanceof String){Properties properties = new Properties();properties.setProperty("com.name", "update");String name = propertySource.getName();if (name.equals("configurationProperties")){continue;}PropertiesPropertySource propertiesPropertySource = new PropertiesPropertySource(name, properties);propertySources.addLast(propertiesPropertySource);}}/* Properties properties = new Properties();properties.setProperty("com.name", "update");PropertiesPropertySource propertySource = new PropertiesPropertySource("Config resource 'class path resource [application.properties]' via location 'optional:classpath:/'", properties);propertySources.addLast(propertySource);*/}
}

疑问:如何让自定义的EnvironmentPostProcessor的生效呢???

  • 是类上加@component注解还是在配置类中注册这个bean呢:你会发现都不行

原因介绍

  • EnvironmentPostProcessor的实现要想执行要么经过反射创建实例,要么走正常的流程创建bean;而不管是类上加@component注解还是在配置类中注册这个bean都是走的是正常的流程而springboot的调用过程如下

    • ......ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments):在这里会读取yml或者properties以及系统属性放到environment中,关键步骤如下:
      • private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {//获取到我们所有的EnvironmentPostProcessor的实现List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();postProcessors.add(this);AnnotationAwareOrderComparator.sort(postProcessors);//遍历执行我们的postProcessEnvironment方法for (EnvironmentPostProcessor postProcessor : postProcessors) {postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());}}
    • ......
    • refreshContext(context):在这里会走正常的创建bean的流程,此刻已经不会执行里面postProcessEnvironment方法 

解决办法

  • 也就是说我们需要在loadPostProcessors时就必须将我们自定义的实现实例化好,下面咱们走一下loadPostProcessors的流程

    • #ConfigFileApplicationListener.loadPostProcessors

List<EnvironmentPostProcessor> loadPostProcessors() {return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());}
  • #SpringFactoriesLoader.loadFactories(具体解析可以看一下前面,这里不再介绍)

    public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {Assert.notNull(factoryType, "'factoryType' must not be null");ClassLoader classLoaderToUse = classLoader;if (classLoaderToUse == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);if (logger.isTraceEnabled()) {logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);}List<T> result = new ArrayList<>(factoryImplementationNames.size());for (String factoryImplementationName : factoryImplementationNames) {result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));}AnnotationAwareOrderComparator.sort(result);return result;}
  • 也就是说必须要把我们的这个EnvironmentPostProcessor的实现的注册放在spring.factories中.只有这样,才能在调用EnvironmentPostProcessor.postProcessEnvironment方法之前创建好我们的实现bean;可以在EnvironmentPostProcessor的实现里做一些我们的操作.

  • #loadFactories#loadFactoryNames返回结果如下--->然后会通过反射实例化返回最终的结果

spring.factories加载原理以及自定义EnvironmentPostProcessor相关推荐

  1. Spring容器加载时执行自定义的方法

    Spring容器加载时执行自定义的方法 需要实现的接口InitializingBean,ApplicationContextAware 案例 package com.djhu.research.web ...

  2. 对集合变量定义赋值_SpringBoot配置加载原理(自定义加载配置)

    前言 在SpringBoot应用程序中会有很多的地方定义配置文件,如: bootstrap.properties application.properties 或者是SpringCloud中定义的远程 ...

  3. spring bean加载原理

    简单的分析了一下spring bean的加载原理,属于个人的理解,源码比这个要复杂的多: spring的配置文件applicationContext.xml的内容如下: <?xml versio ...

  4. spring security加载原理(基于springboot)

    一.基本架构 二.自动配置原理 依据 Spring Boot 自动配置原理,其会自动加载spring-boot-autoconfigure.jar中/META-INF/spring.factories ...

  5. Spring 配置文件加载原理

    参考:准备Spring Boot的环境 1 核心原理 ⭐️1 在SpringBoot的环境准备阶段的后期, 发布一个ApplicationEnvironmentPreparedEvent事件 ⭐️2 ...

  6. spring bean的懒加载原理

    spring bean的懒加载原理 1 普通的bean的 初始化是在初始化阶段开始执行的,而被lazy-init修饰的bean则是从容器第一次进行context.getbean("" ...

  7. Spring的加载机制导致不同SpringBoot启动方式下@Value注解失效

    问题参考链接:https://blog.csdn.net/u011958281/article/details/81531676 背景: 项目使用application.yml文件自定义参数,我在配置 ...

  8. 面试-JVM-类加载-类加载器--自定义类加载器-JVM调优

    文章目录 ==类加载== 谈谈你对类文件结构的理解?有哪些部分组成? 谈谈你对类加载机制的了解? 编写java代码是如何运行起来的? 类加载机制 类加载各阶段的作用分别是什么? 有哪些类加载器?分别有 ...

  9. JPA/hibernate懒加载原理分析及JSON格式API反序列化时连环触发懒加载问题的解决

    什么是懒加载 JPA是java持久层的API,也就是java官方提供的一个ORM框架,Spring data jpa是spring基于hibernate开发的一个JPA框架.Spring data j ...

最新文章

  1. 6.2 IP子网划分
  2. WINCE--VS2005不能连接连线调试
  3. 解决idea中执行maven命令失败的问题
  4. Wifitap是一个WiFi注入工具集常用命令集合大学霸IT达人
  5. matlab中非0即1函数,matlab 中统计一个数组中非零元素个素的函数名称是什么?
  6. php变量赋值有几种,【后端开辟】php变量赋值体式格局有几种
  7. 安装smac的注意事项
  8. php u6536编码转,详谈PHP编码转换问题
  9. matlab loopcount,求助一个数值积分问题,用matlab的quadgk函数来计算,谢谢!
  10. android数字累加,Android自己设置View之数字自动增长
  11. 一文说尽C++赋值运算符重载函数(operator=)
  12. 计算机-p命令,OD(电脑命令)_百度百科
  13. Comet 反Ajax: 基于jQuery与PHP实现Ajax长轮询(LongPoll)
  14. php多个地方ping,同时ping多个ip找了最快的ip网的php实例-PHP源码
  15. js对象取值的两种方式及区别
  16. linux 扩展内存,linux扩展虚拟内存
  17. 区块链矿池(pool)汇总(不定期更新,欢迎评论区留言)
  18. C ++ Singleton设计模式
  19. MongoDB如何释放空闲空间?
  20. Unity中LitJson的使用

热门文章

  1. huffman(赫夫曼编码)之C/C++实现
  2. 明年1月1日起新车标配EDR或行车记录仪,产品形态多元化备战
  3. matlab报错集锦,matlab编程笔记:错误集锦
  4. 统计书籍推荐之大样本理论
  5. windows 无效驱动器 问题
  6. Miracast分析
  7. 2021版网络安全等级测评师培训教材(初级)
  8. miller-rabin测素数与pollard-rho分解质因数
  9. 四象限法则定量分析法,如何客观划分需求优先级?
  10. SMO算法最通俗易懂的解释