一:系统初始化器介绍
- 一:类名:ApplicationContextInitializer
- 二:介绍:Spring容器刷新之前执行的一个回调函数
- 三:作用:向SpringBoot容器中注册属性
- 四:使用:继承接口自定义实现
使用示例(自定义系统初始化器):
第一种:spring.factories中配置
Firstinitializer类
package com.example.demo.initializer;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import java.util.HashMap;
import java.util.Map;
@Order(1)
public class Firstinitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Map map = new HashMap<>();
map.put("key1","value1");
PropertySource<?> propertySource = new MapPropertySource("firstinitializer",map);
environment.getPropertySources().addLast(propertySource);
System.out.println("run firstinitializer");
}
}
resources目录下新建 : META-INF/spring.factories
org.springframework.context.ApplicationContextInitializer=com.example.demo.initializer.Firstinitializer
第二种:启动类中配置
SecondInitializer类
package com.example.demo.initializer;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import java.util.HashMap;
import java.util.Map;
@Order(2)
public class SecondInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Map map = new HashMap<>();
map.put("key2","value2");
PropertySource<?> propertySource = new MapPropertySource("secondinitializer",map);
environment.getPropertySources().addLast(propertySource);
System.out.println("run secondinitializer");
}
}
启动类
package com.example.demo;
import com.example.demo.initializer.SecondInitializer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(DemoApplication.class);
springApplication.addInitializers(new SecondInitializer());
springApplication.run(args);
}
}
第三种:application.properties中配置
ThirdInitializer类
package com.example.demo.initializer;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import java.util.HashMap;
import java.util.Map;
@Order(3)
public class ThirdInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Map map = new HashMap<>();
map.put("key3","value3");
PropertySource<?> propertySource = new MapPropertySource("thirdinitializer",map);
environment.getPropertySources().addLast(propertySource);
System.out.println("run thirdinitializer");
}
}
application.properties
context.initializer.classes=com.example.demo.initializer.ThirdInitializer
测试类
TestService
package com.example.demo.service;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class TestService implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public String test1(){
return applicationContext.getEnvironment().getProperty("key1");
}
public String test2(){
return applicationContext.getEnvironment().getProperty("key2");
}
public String test3(){
return applicationContext.getEnvironment().getProperty("key3");
}
}
tips
- 都要实现ApplicationContextInitializer接口
- Order值越小越先执行
- application.properties中定义的优先于其他方式
二:SpringFactoriesLoader介绍
中文释义:
* 框架内内部使用的通用工厂加载机制。
* * <p>{@code SpringFactoriesLoader} {@linkplain #loadFactories 加载} 并实例化
* 来自 {@value #FACTORIES_RESOURCE_LOCATION} 文件的给定类型的工厂
* 可能出现在类路径中的多个 JAR 文件中。 {@code spring.factories}
* 文件必须是 {@link Properties} 格式,其中键是完全限定的
* 接口或抽象类的名称,值是逗号分隔的列表
* 实现类名。 例如:
* * <pre class="code">example.MyService=example.MyServiceImpl1,example.MyServiceImpl2</pre>
* * 其中 {@code example.MyService} 是接口的名称,而 {@code MyServiceImpl1}
* 和 {@code MyServiceImpl2} 是两个实现。
---------------------------------------------------------------------------------------------------------------------------------------------------
总结下就是
* 一:框架内部使用的通用工厂加载机制
* 二:从classpath下多个jar包特定的位置读取文件并初始化类
* 三:文件内容必须是KV形式,即properties类型
* 四:key是全限定名(抽象类|接口)、value是实现(多个实现用逗号分割)
探索什么时候进行的系统初始化器的加载
//第一步
SpringApplication.run(MystuflinkApplication.class, args);
//第二步
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
//第三步
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
//第四步
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
//第五步
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
/*......*/
//通过getSpringFactoriesInstances找到所有初始化器,在通过setInitializers进行注册
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
/*......*/
}
//5.1
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
//5.2
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
//获取当前类加载器
ClassLoader classLoader = getClassLoader();
//通过ApplicationContextInitializer.class的实现类的全限定名称
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//依次对系统初始化器创建实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//对实例进行排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
//5.3
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
//获取的类加载器
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
//获得ApplicationContextInitializer的类名
String factoryTypeName = factoryType.getName();
//loadSpringFactories获得所有的META-INF/spring.factories下的内容
//getOrDefault方法通过上一步的内容进行筛选获取属于ApplicationContextInitializer子类的全限定名
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
//5.4
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
//是否缓存过META-INF/spring.factories信息
if (result != null) {
return result;
}
result = new HashMap<>();
try {
//初始加载,FACTORIES_RESOURCE_LOCATION=”META-INF/spring.factories“
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
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();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
//缓存起来
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
loadFactories流程图
三:系统初始化器原理解析
中文释义
* 用于初始化 Spring {@link ConfigurableApplicationContext} 的回调接口
* 在 {@linkplain ConfigurableApplicationContext#refresh() 刷新}之前。
*
* <p>通常用于需要一些编程初始化的 Web 应用程序中
* 应用程序上下文。 例如,注册财产来源或激活
* 针对 {@linkplain ConfigurableApplicationContext#getEnvironment() 的配置文件
* 上下文的环境}。 请参阅 {@code ContextLoader} 和 {@code FrameworkServlet} 支持
* 分别用于声明“contextInitializerClasses”上下文参数和初始化参数。
*
* <p>{@code ApplicationContextInitializer} 鼓励处理器检测
* Spring 的 {@link org.springframework.core.Ordered Ordered} 接口是否已经
* 已实现或如果 {@link org.springframework.core.annotation.Order @Order}
* 存在注释并在调用之前相应地对实例进行排序。
---------------------------------------------------------------------------------------------------------------------------------------------------
总结下就是
1、上下文刷新即refresh方法前被调用
2、用来编码设置一些属性变量通常用在web环境中
3、可以通过实现order接口进行排序
探索什么时候进行的系统初始化器的使用呢
//1
SpringApplication.run(MystuflinkApplication.class, args);
//2
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
//3
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
//4
public ConfigurableApplicationContext run(String... args) {
/*......*/
try {
/*......*/
//此处是系统初始化器被调用的地方
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//应用上下文刷新
refreshContext(context);
afterRefresh(context, applicationArguments);
/*......*/
}
catch (Throwable ex) {
/*......*/
}
/*......*/
return context;
}
//5
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
/*......*/
//开始调用
applyInitializers(context);
/*......*/
}
//6
protected void applyInitializers(ConfigurableApplicationContext context) {
//getInitializers方法拿到所有的系统初始化器
for (ApplicationContextInitializer initializer : getInitializers()) {
//获取泛型类型为ApplicationContextInitializer的初始化器
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
//执行调用
initializer.initialize(context);
}
}
拓展
在第一栏目中的使用示例中有讲了三种系统初始化器的实现方式,以上讲解的是第一种(即:通过META-INF/spring.factories文件中配置的方式),另外两种都是第一种方式的拓展,下面来讲讲区别在哪里:
- 通过在启动类中配置
SpringApplication springApplication = new SpringApplication();
springApplication.addInitializers(...);
springApplication.run();
//这个比较好理解,因为在SpringApplication构造器中已经初始化好了initializers属性,这里直接进行add即可,然后通过调用run进行执行
- 通过application.properties文件配置
//1 打开DelegatingApplicationContextInitializer类
public class DelegatingApplicationContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
/*......*/
//因为order为0,所以也就解释了为什么通过application.properties配置的优先级更高了
private int order = 0;
/*......*/
private static final String PROPERTY_NAME = "context.initializer.classes";
/*......*/
}
//2 spring-boot-*.jar
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
**org.springframework.boot.context.config.DelegatingApplicationContextInitializer**,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
//这个也很容易看出和第一步的加载逻辑其实是一样的
//3执行逻辑,在SpringApplication的run方法内调用了prepareContext方法,prepareContext方法调用了applyInitializers方法,在applyInitializers内部执行initializer.initialize(context);即系统初始化器的调用
//4
public void initialize(ConfigurableApplicationContext context) {
ConfigurableEnvironment environment = context.getEnvironment();
//获得 context.initializer.classes对应的一个或多个系统初始化器
List<Class<?>> initializerClasses = getInitializerClasses(environment);
if (!initializerClasses.isEmpty()) {
//执行调用
applyInitializerClasses(context, initializerClasses);
}
}
//4.1 PROPERTY_NAME="context.initializer.classes";
private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
//获取application.properties中context.initializer.classes的value值
String classNames = env.getProperty(PROPERTY_NAME);
List<Class<?>> classes = new ArrayList<>();
if (StringUtils.hasLength(classNames)) {
//获得以逗号分割的全路径类名的数组,并循环
for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
//在getInitializerClass方法通过forname方法获得Class对象
classes.add(getInitializerClass(className));
}
}
return classes;
}
//4.2
private Class<?> getInitializerClass(String className) throws LinkageError {
try {
Class<?> initializerClass = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
Assert.isAssignable(ApplicationContextInitializer.class, initializerClass);
return initializerClass;
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException("Failed to load context initializer class [" + className + "]", ex);
}
}
//5 获系统初始化器的实例对象 并调用初始化方法
private void applyInitializerClasses(ConfigurableApplicationContext context, List<Class<?>> initializerClasses) {
Class<?> contextClass = context.getClass();
List<ApplicationContextInitializer<?>> initializers = new ArrayList<>();
for (Class<?> initializerClass : initializerClasses) {
//instantiateInitializer 获系统初始化器的实例对象
initializers.add(instantiateInitializer(contextClass, initializerClass));
}
//初始化方法调用
applyInitializers(context, initializers);
}
//6 初始化方法调用
private void applyInitializers(ConfigurableApplicationContext context,
List<ApplicationContextInitializer<?>> initializers) {
initializers.sort(new AnnotationAwareOrderComparator());
for (ApplicationContextInitializer initializer : initializers) {
//initialize方法调用
initializer.initialize(context);
}
}
四:总结
调用流程
实现方式:
- 定义在spring.factories文件中被SpringFactoriesLoader发现注册
- SpringApplication初始化完毕后手动添加
- 通过application.properties定义成环境变量被DelegatingApplicationContextInitializer发现注册
回顾&面试
实现回顾
- org.springframework.context.ApplicationContextInitializer=com.wwy.myspringboot.initializer.FirstInitializer
- springApplication.addInitializers(…);
- context.initializer.classes=com.wwy.myspringboot.initializer.FirstInitializer
面试题
- 介绍下SpringFactoriesLoader?
- SpringFactoriesLoader如何加载工厂类?
- 系统初始化器的作用?
- 系统初始化器的调用时机?
- 如何自定义实现系统初始化器?
- 自定义实现系统初始化器有哪些注意事项?