ApplicationContextInitializer的理解和使用

一、ApplicationContextInitializer 介绍

1.1 作用

ApplicationContextInitializer 接口用于在 Spring 容器刷新之前执行的一个回调函数,通常用于向 SpringBoot 容器中注入属性。

1.2 springboot中ApplicationContextInitializer(系统初始化器)的三种加载方式

  • spring.factories中配置(springboot会扫描所有jar包下的META-INF/spring.factorties)

resources目录下新建 : META-INF/spring.factories(key为org.springframework.context.ApplicationContextInitializer)

org.springframework.context.ApplicationContextInitializer=com.example.demo.initializer.Firstinitializer
  • 启动类中配置
 1 package com.example.demo;
 2 import com.example.demo.initializer.SecondInitializer;
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 
 6 @SpringBootApplication
 7 public class DemoApplication {
 8 
 9     public static void main(String[] args) {
10         SpringApplication springApplication = new SpringApplication(DemoApplication.class);
11         springApplication.addInitializers(new SecondInitializer());
12         springApplication.run(args);
13     }
14 
15 }
  • application.properties中配置

key为context.initializer.classes

context.initializer.classes=com.example.demo.initializer.ThirdInitializer

1.3 springboot下的spring.factories

spring-boot-2.1.6.RELEASE.jar中的spring.factories文件定义的ApplicationContextInitializer实现如下:

org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

1.4 内置实现类

DelegatingApplicationContextInitializer

使用环境属性 context.initializer.classes 指定的初始化器(initializers)进行初始化工作,如果没有指定则什么都不做。

通过它使得我们可以把自定义实现类配置在 application.properties 里成为了可能。

ContextIdApplicationContextInitializer

设置Spring应用上下文的ID,会参照环境属性。至于Id设置为什么值,将会参考环境属性:
* spring.application.name
* vcap.application.name
* spring.config.name
* spring.application.index
* vcap.application.instance_index
    
如果这些属性都没有,ID 使用 application。

ConfigurationWarningsApplicationContextInitializer

对于一般配置错误在日志中作出警告

ServerPortInfoApplicationContextInitializer

 将内置 servlet容器实际使用的监听端口写入到 Environment 环境属性中。这样属性 local.server.port 就可以直接通过 @Value 注入到测试中,或者通过环境属性 Environment 获取。

SharedMetadataReaderFactoryContextInitializer

创建一个 SpringBoot和ConfigurationClassPostProcessor 共用的 CachingMetadataReaderFactory对象。实现类为:ConcurrentReferenceCachingMetadataReaderFactory

ConditionEvaluationReportLoggingListener

将 ConditionEvaluationReport写入日志。

二、实现方式

首先新建三个自定义类,实现 ApplicationContextInitializer 接口

 1 public class FirstInitializer implements ApplicationContextInitializer {
 2 
 3     @Override
 4     public void initialize(ConfigurableApplicationContext applicationContext) {
 5         ConfigurableEnvironment environment = applicationContext.getEnvironment();
 6 
 7         Map<String, Object> map = new HashMap<>();
 8         map.put("key1", "First");
 9 
10         MapPropertySource mapPropertySource = new MapPropertySource("firstInitializer", map);
11         environment.getPropertySources().addLast(mapPropertySource);
12 
13         System.out.println("run firstInitializer");
14     }
15 
16 }
17 
18 public class SecondInitializer implements ApplicationContextInitializer {
19 
20     @Override
21     public void initialize(ConfigurableApplicationContext applicationContext) {
22         ConfigurableEnvironment environment = applicationContext.getEnvironment();
23 
24         Map<String, Object> map = new HashMap<>();
25         map.put("key1", "Second");
26 
27         MapPropertySource mapPropertySource = new MapPropertySource("secondInitializer", map);
28         environment.getPropertySources().addLast(mapPropertySource);
29 
30         System.out.println("run secondInitializer");
31     }
32 
33 }
34 
35 public class ThirdInitializer implements ApplicationContextInitializer {
36 
37     @Override
38     public void initialize(ConfigurableApplicationContext applicationContext) {
39         ConfigurableEnvironment environment = applicationContext.getEnvironment();
40 
41         Map<String, Object> map = new HashMap<>();
42         map.put("key1", "Third");
43 
44         MapPropertySource mapPropertySource = new MapPropertySource("thirdInitializer", map);
45         environment.getPropertySources().addLast(mapPropertySource);
46 
47         System.out.println("run thirdInitializer");
48     }
49 
50 }

2.1 在 resources/META-INF/spring.factories 中配置

org.springframework.context.ApplicationContextInitializer=com.learn.springboot.initializer.FirstInitializer

2.2 在 mian 函数中添加

 1 @SpringBootApplication
 2 public class SpringbootApplication {
 3 
 4     public static void main(String[] args) {
 5 //        SpringApplication.run(SpringbootApplication.class, args);
 6         SpringApplication springApplication = new SpringApplication(SpringbootApplication.class);
 7         springApplication.addInitializers(new SecondInitializer());
 8         springApplication.run();
 9     }
10 
11 }

2.3 在配置文件中配置

context.initializer.classes=com.learn.springboot.initializer.ThirdInitializer

2.4 运行项目,查看控制台:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.5.RELEASE)

run thirdInitializer
run firstInitializer
run secondInitializer

可以看到配置生效了,并且三种配置优先级不一样,配置文件优先级(application.properties)最高,spring.factories 其次,代码add最后。

三、获取属性值

 1 @RestController
 2 public class HelloController {
 3     
 4     private ApplicationContext applicationContext;
 5     
 6     public HelloController(ApplicationContext applicationContext) {
 7         this.applicationContext = applicationContext;
 8     }
 9 
10     @RequestMapping("/getAttributes")
11     public String getAttributes() {
12         String value = applicationContext.getEnvironment().getProperty("key1");
13         System.out.println(value);
14         return value;
15     }
16     
17 }

启动项目,访问http://localhost:8080/getAttributes 查看控制台输出:

Third

发现同名的 key,只会存在一个,并且只存第一次设置的值。

四、通过 @Order 注解修改执行顺序

注:@order 值越小,执行优先级越高

4.1 不同配置方式下,执行顺序

 1 @Order(1)
 2 public class SecondInitializer implements ApplicationContextInitializer {
 3     ......
 4 }
 5 
 6 @Order(2)
 7 public class FirstInitializer implements ApplicationContextInitializer {
 8     ......
 9 }
10 
11 @Order(3)
12 public class ThirdInitializer implements ApplicationContextInitializer {
13     ......
14 }

运行项目,查看控制台:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.5.RELEASE)

run thirdInitializer
run secondInitializer
run firstInitializer

可以看到通过 @Order * 注解是可以改变spring.factories* 和代码形式的执行顺序的,但是application.properties 配置文件的优先级还是最高的。

4.2 同一配置下,执行顺序

新建实现类

 1 @Order(1)
 2 public class FourthInitializer implements ApplicationContextInitializer {
 3 
 4     @Override
 5     public void initialize(ConfigurableApplicationContext applicationContext) {
 6         ConfigurableEnvironment environment = applicationContext.getEnvironment();
 7 
 8         Map<String, Object> map = new HashMap<>();
 9         map.put("key1", "Fourth");
10 
11         MapPropertySource mapPropertySource = new MapPropertySource("FourthInitializer", map);
12         environment.getPropertySources().addLast(mapPropertySource);
13 
14         System.out.println("run fourthInitializer");
15     }
16 
17 }

在application.properties 文件中配置

context.initializer.classes=com.learn.springboot.initializer.ThirdInitializer,com.learn.springboot.initializer.FourthInitializer

运行项目,查看控制台:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.5.RELEASE)

run fourthInitializer
run thirdInitializer
run secondInitializer
run firstInitializer

五、系统初始化器原理解析

5.1 手动在main函数中添加原理分析

在之前我们知道 SpringApplication 初始化之后,就已经把 META-INF/spring.factories 中配置的初始化实现类添加到 initializers 列表中了,然后通过 addInitializers 方法,添加自定义的实现类:

1  public static void main(String[] args) {
2      // SpringApplication.run(SpringbootApplication.class, args);
3      SpringApplication springApplication = new SpringApplication(SpringbootApplication.class);
4      springApplication.addInitializers(new ThirdInitializer());
5      springApplication.run();
6  }
7  public void addInitializers(ApplicationContextInitializer<?>... initializers) {
8      this.initializers.addAll(Arrays.asList(initializers));
9  }

在SpringApplication类中的run方法,有如下相关代码:

 1  public ConfigurableApplicationContext run(String... args) {
 2         StopWatch stopWatch = new StopWatch();
 3         stopWatch.start();
 4         ConfigurableApplicationContext context = null;
 5         Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
 6         this.configureHeadlessProperty();
 7         SpringApplicationRunListeners listeners = this.getRunListeners(args);
 8         listeners.starting();
 9  
10         Collection exceptionReporters;
11         try {
12             ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
13             ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
14             this.configureIgnoreBeanInfo(environment);
15             Banner printedBanner = this.printBanner(environment);
16             context = this.createApplicationContext();
17             exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
18             this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);  #刷新容器前,初始化
19             this.refreshContext(context);
20             this.afterRefresh(context, applicationArguments);
21             stopWatch.stop();
22             if (this.logStartupInfo) {
23                 (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
24             }
25  
26             listeners.started(context);
27             this.callRunners(context, applicationArguments);
28         } catch (Throwable var10) {
29             this.handleRunFailure(context, var10, exceptionReporters, listeners);
30             throw new IllegalStateException(var10);
31         }
32  
33         try {
34             listeners.running(context);
35             return context;
36         } catch (Throwable var9) {
37             this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
38             throw new IllegalStateException(var9);
39         }
40     }

prepareContext方法的核心代码如下:

 1 private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
 2         context.setEnvironment(environment);
 3         this.postProcessApplicationContext(context);
 4         this.applyInitializers(context); #遍历initializers集合中的SpringApplicationInitializer实例
 5         listeners.contextPrepared(context);
 6         if (this.logStartupInfo) {
 7             this.logStartupInfo(context.getParent() == null);
 8             this.logStartupProfileInfo(context);
 9         }
10  
11         ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
12         beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
13         if (printedBanner != null) {
14             beanFactory.registerSingleton("springBootBanner", printedBanner);
15         }
16  
17         if (beanFactory instanceof DefaultListableBeanFactory) {
18             ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
19         }
20  
21         Set<Object> sources = this.getAllSources();
22         Assert.notEmpty(sources, "Sources must not be empty");
23         this.load(context, sources.toArray(new Object[0]));
24         listeners.contextLoaded(context);
25     }

SpringApplication中的applyInitializers方法就是遍历initializers集合中的SpringApplicationInitializer实例,调用其initialize方法。

 1 protected void applyInitializers(ConfigurableApplicationContext context) {
 2         Iterator var2 = this.getInitializers().iterator();
 3  
 4         while(var2.hasNext()) {
 5             ApplicationContextInitializer initializer = (ApplicationContextInitializer)var2.next();
 6             Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);
 7             Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
 8             initializer.initialize(context);
 9         }
10  
11     }

综上,ApplicationContextInitializer的调用时机,是在容器刷新之前的prepareContext方法,通过获取ApplicationContextInitializer的集合,遍历调用initialize方法。

5.2 在 resources/META-INF/spring.factories 中配置实现原理

实例化SpringApplication对象使,其构造方法如下

 1 public SpringApplication(Class... primarySources) {
 2         this((ResourceLoader)null, primarySources);
 3     }
 4  
 5 public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
 6         this.sources = new LinkedHashSet();
 7         this.bannerMode = Mode.CONSOLE;
 8         this.logStartupInfo = true;
 9         this.addCommandLineProperties = true;
10         this.addConversionService = true;
11         this.headless = true;
12         this.registerShutdownHook = true;
13         this.additionalProfiles = new HashSet();
14         this.isCustomEnvironment = false;
15         this.resourceLoader = resourceLoader;
16         Assert.notNull(primarySources, "PrimarySources must not be null");
17         this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
18         this.webApplicationType = WebApplicationType.deduceFromClasspath();
19         this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
20         this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
21         this.mainApplicationClass = this.deduceMainApplicationClass();
22     }

其中会调用setInitializers方法设置SpringApplicationInitializer集合;

getSpringFactoriesInstances(ApplicationContextInitializer.class)逻辑如下:

  1. 该方法会扫描所有的META-INF/spring.factorties文件
  2. 获取key为org.springframework.context.ApplicationContextInitializer的所有属性值(本质上是ApplicationContextInitializer的实现类的类全名)
  3. 然后使用其对应类的无参构造器进行反射实例化并进行排序,然后设置到SpringApplication实例中的initializers集合中;

所以我们创建spring.factories文件中的自定义ApplicationContextInitializer会加入到initializers集合中。

在 SpringApplication 初始化时通过 SpringFactoriesLoader 获取到配置在 META-INF/spring.factories 文件中的 ApplicationContextInitializer 的所有实现类.

 1 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
 2     ......
 3     // 设置系统初始化器
 4     setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
 5     ......
 6 }
 7 // 获取工厂实例对象
 8 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
 9     return getSpringFactoriesInstances(type, new Class<?>[] {});
10 }
11 // 获取工厂实例对象
12 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
13     // 获取类加载器
14     ClassLoader classLoader = getClassLoader();
15     // 使用名称并确保唯一以防止重复
16     Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
17     // 创建工厂实例对象
18     List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
19     // 对工厂实例对象列表进行排序
20     AnnotationAwareOrderComparator.sort(instances);
21     return instances;
22 }
23 
24 // 创建工厂实例对象
25 private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
26         ClassLoader classLoader, Object[] args, Set<String> names) {
27     List<T> instances = new ArrayList<>(names.size());
28     for (String name : names) {
29         try {
30             Class<?> instanceClass = ClassUtils.forName(name, classLoader);
31             Assert.isAssignable(type, instanceClass);
32             Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
33             T instance = (T) BeanUtils.instantiateClass(constructor, args);
34             instances.add(instance);
35         }
36         catch (Throwable ex) {
37             throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
38         }
39     }
40     return instances;
41 }

在 apllication启动的run 方法中回调 ApplicationContextInitializer 接口函数

 1 public ConfigurableApplicationContext run(String... args) {
 2     ......
 3     // 准备上下文环境注入系统初始化信息 
 4     prepareContext(context, environment, listeners, applicationArguments, printedBanner);
 5     ......
 6 }
 7 private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
 8         SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
 9     ......
10     // 应用初始化器   
11     applyInitializers(context);
12     ......
13 }
14 protected void applyInitializers(ConfigurableApplicationContext context) {
15     for (ApplicationContextInitializer initializer : getInitializers()) {
16         // 判断子类是否是 ConfigurableApplicationContext 类型
17         Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
18                 ApplicationContextInitializer.class);
19         Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
20         // 回调 ApplicationContextInitializer接口的 initialize 方法
21         initializer.initialize(context);
22     }
23 }

获取初始化器列表

 1 // 获取在 SpringApplication 构造函数中设置的初始化器列表
 2 public Set<ApplicationContextInitializer<?>> getInitializers() {
 3     return asUnmodifiableOrderedSet(this.initializers);
 4 }
 5 // 对初始化器列表进行排序
 6 private static <E> Set<E> asUnmodifiableOrderedSet(Collection<E> elements) {
 7     List<E> list = new ArrayList<>(elements);
 8     list.sort(AnnotationAwareOrderComparator.INSTANCE);
 9     return new LinkedHashSet<>(list);
10 }

5.3 在配置文件中配置实现原理

在配置文件中配置方式,主要通过内置的 DelegatingApplicationContextInitializer 实现的,它实现了 Order 方法,所以优先级最高。

1 private int order = 0;    
2 
3 @Override
4 public int getOrder() {
5     return this.order;
6 }

然后我们看下它的 initialize方法实现:

 1 @Override
 2 public void initialize(ConfigurableApplicationContext context) {
 3     // 获取上下文环境变量
 4     ConfigurableEnvironment environment = context.getEnvironment();
 5     // 从上下文环境变量中获取指定初始化类列表
 6     List<Class<?>> initializerClasses = getInitializerClasses(environment);
 7     if (!initializerClasses.isEmpty()) {
 8         // 应用初始化器
 9         applyInitializerClasses(context, initializerClasses);
10     }
11 }

从上下文环境变量获取指定的属性名,并实例化对象

 1 private static final String PROPERTY_NAME = "context.initializer.classes";
 2 
 3 private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
 4     // 从上下文环境变量获取指定的属性名
 5     String classNames = env.getProperty(PROPERTY_NAME);
 6     List<Class<?>> classes = new ArrayList<>();
 7     if (StringUtils.hasLength(classNames)) {
 8         // 将逗号分割的属性值逐个取出
 9         for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
10             // 实例化对象并添加到列表中
11             classes.add(getInitializerClass(className));
12         }
13     }
14     return classes;
15 }
16 private Class<?> getInitializerClass(String className) throws LinkageError {
17     try {
18         Class<?> initializerClass = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
19         Assert.isAssignable(ApplicationContextInitializer.class, initializerClass);
20         return initializerClass;
21     }
22     catch (ClassNotFoundException ex) {
23         throw new ApplicationContextException("Failed to load context initializer class [" + className + "]", ex);
24     }
25 }

 从容器上下文中的Environment中获取配置名为context.initializer.classes的属性值(多个用,隔开,实际配置的是类全名),如果存在就通过反射实例化这些ApplicationContextInitialzier对象并排序,遍历并调用其initialize方法。

该类实际上是一个委托类,将实际的初始化工作交给了context.initializer.classes环境变量指定的ApplicationContextInitialize对象

springboot中自带的DelegatingApplicationContextInitializer类的排序值为0,是springboot自带的ApplicationContextInitializer中排序最小,最先执行的类。(如果ApplicationContextInitializer没有实现Orderd接口,那么其排序值默认是最大,最后执行)

6.总结

所以可以得到其执行顺序如下:

1.application.properties中定义的ApplicationContextInitializer优先于其他定义的方式

因为配置中的ApplicationContextInitializer是通过DelegatingApplicationContextInitializer实现,而DelegatingApplicationContextInitializer的排序最小(order=0),在默认的ApplicationContextInitializer第一个执行。(同样通过配置文件添加的ApplicationContextInitializer通过@Order实现排序执行)

2.自己创建的spring.factories文件中自定义的ApplicationContextInitializer和通过addInitializers方法加入自定义的ApplicationContextInitializer(若未改order,按照添加顺序执行),order越小执行越早。若都未改order,则自己创建的spring.factories中定义的要更早执行(因为是在构造方法中添加的)

7.Spring Factories实现原理

 spring-core包里定义了SpringFactoriesLoader类,这个类实现了检索META-INF/spring.factories文件,并获取指定接口的配置的功能。在这个类中定义了两个对外的方法:

  • loadFactories :根据接口类获取其实现类的实例,这个方法返回的是对象列表。
  • loadFactoryNames :根据接口获取其接口类的名称,这个方法返回的是类名的列表。

上面的两个方法的关键都是从指定的ClassLoader中获取spring.factories文件,并解析得到类名列表,具体代码如下:

 1    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
 2         MultiValueMap<String, String> result = cache.get(classLoader);
 3         if (result != null) {
 4             return result;
 5         }
 6         try {
 7             Enumeration<URL> urls = (classLoader != null ?
 8                     //遍历整个ClassLoader中所有jar包下的spring.factories文件
 9                     classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
10                     ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
11             result = new LinkedMultiValueMap<>();
12             while (urls.hasMoreElements()) {
13                 URL url = urls.nextElement();
14                 UrlResource resource = new UrlResource(url);
15                 Properties properties = PropertiesLoaderUtils.loadProperties(resource);
16                 for (Map.Entry<?, ?> entry : properties.entrySet()) {
17                     String factoryClassName = ((String) entry.getKey()).trim();
18                     for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
19                         result.add(factoryClassName, factoryName.trim());
20                     }
21                 }
22             }
23             cache.put(classLoader, result);
24             return result;
25         }
26         catch (IOException ex) {
27             throw new IllegalArgumentException("Unable to load factories from location [" +
28                     FACTORIES_RESOURCE_LOCATION + "]", ex);
29         }
30     }

从代码中我们可以知道,在这个方法中会遍历整个ClassLoader中所有jar包下的spring.factories文件。也就是说我们可以在自己的jar中配置spring.factories文件,不会影响到其它地方的配置,也不会被别人的配置覆盖。spring.factories的是通过Properties解析得到的,所以我们在写文件中的内容都是安装下面这种方式配置的:

com.xxx.interface=com.xxx.classname

如果一个接口希望配置多个实现类,可以使用’,’进行分割。

 

参考:

https://blog.csdn.net/weixin_45994575/article/details/124596081

https://blog.csdn.net/u014520047/article/details/94553296

https://blog.csdn.net/cristianoxm/article/details/119751841

 

posted @ 2022-06-29 15:39  Boblim  阅读(1134)  评论(0编辑  收藏  举报