目录
1.1 SpringApplication#getSpringFactoriesInstances
1.1.1SpringFactoriesLoader#loadFactoryNames-->loadSpringFactories
1.1.2 createSpringFactoriesInstances
疑问:如何让自定义的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返回结果如下--->然后会通过反射实例化返回最终的结果