spring.factories加载原理以及自定义EnvironmentPostProcessor

目录

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为null
  setInitializers((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,也就是一个线程安全的map
	private 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());
	}

    // 加载所有的springFactories
	private 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.factories
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				// 解析properties
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				// 将所有的key放入result
				for (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.ElasticsearchStarterAutoConfiguration
						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);
		}
	}
    
	/**
	 * 通过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的classLoader
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		// 加载所有的META-INF/spring.factories并解析,获取其配置的factoryNames
        //factoryType为EnvironmentPostProcessor.class
		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;
	}

	


	

	// 实例化工厂,根据
	@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{

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        //得到的是PropertySource集合,当前也可以通过指定具体的name的带一个具体的PropertySource
        PropertySource<?> 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返回结果如下--->然后会通过反射实例化返回最终的结果

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值