系列文章目录
文章目录
前言
实现系统初始化器、分析SpringBoot源码和系统初始化器的原理来介绍SpringBoot中的系统初始化器
参考资料:https://www.imooc.com/
参考文章:https://blog.csdn.net/qq_34886352/article/details/104949485
一、系统初始化器实现
系统初始化器的类名是ApplicationContextInitializer,官方描述为Spring容器刷新之前执行的一个回调函数,其作用是向SpringBoot容器中注册属性,使用方法为继承接口自定义实现。
下面通过三种方式自定义实现系统初始化器
方法一:spring.factories文件注入
//新建一个类,实现ApplicationContextInitializer接口
@Order(1)
public class FirstInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
//从上下文获取到程序环境
ConfigurableEnvironment environment = applicationContext.getEnvironment();
//创建需要注入的属性
Map<String,Object> map = new HashMap<>();
map.put("key1","value1");
//将属性封装成配置项
MapPropertySource mapPropertySource = new MapPropertySource("firstInitializer",map);
//添加到项目环境配置的最末端
environment.getPropertySources().addLast(mapPropertySource);
//控制台输出提示
System.out.println("run firstInitializer");
}
}
#在resources的META-INF目录下的spring.factories文件中添加初始化器的实现类的完整路径
org.springframework.context.ApplicationContextInitializer=com.mooc.sb2.initializer.FirstInitializer
方法二:在SpringApplication中手动注入
@Order(2)
public class SecondInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Map<String,Object> map = new HashMap<>();
map.put("key2","value2");
MapPropertySource mapPropertySource = new MapPropertySource("secondInitializer",map);
environment.getPropertySources().addLast(mapPropertySource);
System.out.println("run secondInitializer");
}
}
修改启动类中的内容
@SpringBootApplication
@MapperScan("com.mooc.sb2.mapper")
public class Sb2Application {
public static void main(String[] args) {
//启动
SpringApplication springApplication = new SpringApplication(Sb2Application.class);
springApplication.addInitializers(new SecondInitializer());
springApplication.run(args);
}
}
方法三:在application.properties中进行注册
@Order(3)
public class ThirdInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Map<String,Object> map = new HashMap<>();
map.put("key3","value3");
MapPropertySource mapPropertySource = new MapPropertySource("ThirdInitializer",map);
environment.getPropertySources().addLast(mapPropertySource);
System.out.println("run ThirdInitializer");
}
}
#在resources文件中新建一个application.properties文件,配置下面信息
context.initializer.classes=com.mooc.sb2.initializer.ThirdInitializer
测试
//创建service
@Component
public class TestService implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public String test(){
return applicationContext.getEnvironment().getProperty("key3");
}
}
//在controller中新建test方法
@RequestMapping("test")
@ResponseBody
public String test(){
return testService.test();
}
结果:
总结
- 自定义的系统初始化器都要实现ApplicationContextInitializer接口
- Order值越小越先执行
- application.properties中定义的优先于其他方式
自定义的系统初始化器如何被Spring容器识别并加载到Spring容器内的?==》SpringFactoriesLoader
二、SpringFactoriesLoader
SpringFactoriesLoader类的主要作用是通过类路径下的MEAT-INF/spring.factories文件获取工厂类接口的实现类,初始化并保存在缓存中,以供Springboot启动过程中各个阶段的调用。
2.1 源码剖析
首先进入SpringBoot启动类
@SpringBootApplication
@MapperScan("com.mooc.sb2.mapper")
public class Sb2Application {
public static void main(String[] args) {
//进入run方法
SpringApplication.run(Sb2Application.class, args);
}
}
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
//进入同名run方法
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
//1、初始化SpringApplication类
//2、run方法
//而上面已经说了,系统初始化器的作用是从MEAT-INF/spring.factories中获取工厂接口的实现类,
//初始化并保存在缓存中,以供SpringBoot启动过程中使用
//因此只要看SpringApplication构造方法
//进入SpringApplication构造方法
return new SpringApplication(primarySources).run(args);
}
public SpringApplication(Class<?>... primarySources) {
//点this查看
this(null, primarySources);
}
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();
//通过getSpringFactoriesInstances来获取系统初始化器的实现
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
//进入同名方法
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
//获取类加载器
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
//获得所有实现类的全路径名
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//创建实现类的实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//将上面获取到的所有类根据@Order进行排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
getSpringFactoriesInstances中有三个核心的方法
①loadFactoryNames:获取所有实现类的全路径名
②createSpringFactoriesInstances:创建实现类的实例
③sort:将上面获取到的所有类根据@Order进行排序
loadFactoryNames
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
//获取到所需要的工厂类的名字
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
//首先看是否存在缓存
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
//public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
//在上文的实现方法一中正是在META-INF/spring.factories中注入系统初始化器的全路径名
//但是这里并不是只有这一个spring.factories文件,它会获取所有jar包中的META-INF/spring.factories
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
//因此这里循环遍历所有的META-INF/spring.factories文件
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
//转化为kv形式即properties类型
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
//获取key值,例如在上文方法一中,就是org.springframework.context.ApplicationContextInitializer
String factoryTypeName = ((String) entry.getKey()).trim();
//将value的值按照','逗号进行分割
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
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);
}
}
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;
}
2.2 总结
在getSpringFactoriesInstances中分为三步
- loadFactoryNames:获取所有实现类的全路径名
- 获取工厂类的名字
- 加载工厂类
- 看是否存在缓存==》存在就直接返回
- 获取所有jar包中的META-INF/spring.factories文件
- 遍历文件转为properties对象
- 获取key、value值并用’,'将value值分割
- 将结果存入缓存并返回
- createSpringFactoriesInstances:创建实现类的实例
- 根据全路径名依次实例化对象
- sort:将上面获取到的所有类根据@Order进行排序
- 结果排序
三、自定义系统初始化器原理解析
方法一:spring.factories文件注入
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
//在run方法启动时,才真正开始管理springBoot的生命周期
//进入run方法中
return new SpringApplication(primarySources).run(args);
}
//参考博客:https://blog.csdn.net/qq_34886352/article/details/104949485
public ConfigurableApplicationContext run(String... args) {
//创建一个计时器,用来记录SpringBoot的运行时间
StopWatch stopWatch = new StopWatch();
//启动计时器
stopWatch.start();
//配置应用上下文的控制器,可进行一些基础配置的操作,设置上下文ID,设置父应用上下文,添加监听器和刷新容器相关的操作等
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 配置属性
configureHeadlessProperty();
//获取监听器 从路径MEAT-INF/spring.factories中找到所有的SpringApplicationRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
//监视器启动!
listeners.starting();
try {
//封装一下环境
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//打印banner 就是每次启动输出的SpringBoot的图标
Banner printedBanner = printBanner(environment);
//创建应用程序上下文,常用的有2种 web环境的和普通环境的,根据条件不同,生成的结果不一样
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//准备上下文,下面那个就是刷新上下文
//ApplicationContextInitializer接口在刷新上下文(refreshContext)之前起作用
//就是这里没跑了,点进去
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新上下文
refreshContext(context);
//2次刷新
afterRefresh(context, applicationArguments);
//计时器停止
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
进入prepareContext方法
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
//传入的参数就是之前获取的web环境的应用程序上下文,进入!!
//ApplicationContextInitializer也就是从这里要开始起作用了
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
applyInitializers方法
protected void applyInitializers(ConfigurableApplicationContext context) {
//getInitializers() 就是简单的拿出initializers属性,然后根据order属性进行排序
for (ApplicationContextInitializer initializer : getInitializers()) {
//这里会获取实现ApplicationContextInitializer接口时填写的泛型
//如果记不得了 ,全文搜索一下“FirstInitializer”,可以看到我们实现ApplicationContextInitializer时填写的泛型正是“ConfigurableApplicationContext”
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
//判断填入的泛型是否为ConfigurableApplicationContext类或者是它的子类,如果不是停止运行,并报错
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
//这里调用了ApplicationContextInitializer接口强制实现的initialize方法
//在FirstInitializer,实现接口的同时,重写了initialize方法
initializer.initialize(context);
}
}
运行至此,就可以打印出"run firstInitializer"
方法二:在SpringApplication中手动注入
在SpringBoot启动类中直接addInitializers
SpringApplication springApplication = new SpringApplication(Sb2Application.class);
springApplication.addInitializers(new SecondInitializer());
springApplication.run(args);
进入addInitializers方法
public void addInitializers(ApplicationContextInitializer<?>... initializers) {
//其实就是手动把系统初始化器加入到集和中
this.initializers.addAll(Arrays.asList(initializers));
}
在上文中,工厂加载机制中会调用到setInitializers,然后在SpringBoot运行时加载系统初始化器过程中会getInitializers
方法三:在application.properties中进行注册
这种方式是通过DelegatingApplicationContextInitializer类来实现,在这个类中,定义了 private int order = 0; 即通过这种方法实现的优先级最高
@Override
public void initialize(ConfigurableApplicationContext context) {
ConfigurableEnvironment environment = context.getEnvironment();
//环境变量中查找initializer所有类的类型
List<Class<?>> initializerClasses = getInitializerClasses(environment);
if (!initializerClasses.isEmpty()) {
applyInitializerClasses(context, initializerClasses);
}
}
getInitializerClasses:获取application.properties中的配置信息
private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
//从环境变量(我们的配置文件)中查找一个叫PROPERTY_NAME的属性
//PROPERTY_NAME是个常量,值为“context.initializer.classes”
//全局搜索context.initializer.classes,看看出现在哪了,就是配置文件中的属性
String classNames = env.getProperty(PROPERTY_NAME);
List<Class<?>> classes = new ArrayList<>();
if (StringUtils.hasLength(classNames)) {
//将设置的值根据“,”切割 所以我们配置多个ApplicationContextInitializer实现类的时候需要用“,”隔开
for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
//getInitializerClass同名方法,方法的重载,根据字符查找对应的类,并判断是否为ApplicationContextInitializer的实现类
//然后添加到返回对象中
classes.add(getInitializerClass(className));
}
}
return classes;
}
applyInitializerClasses
private void applyInitializerClasses(ConfigurableApplicationContext context, List<Class<?>> initializerClasses) {
Class<?> contextClass = context.getClass();
List<ApplicationContextInitializer<?>> initializers = new ArrayList<>();
for (Class<?> initializerClass : initializerClasses) {
//instantiateInitializer方法是使用BeanUtils工具类实例化ApplicationContextInitializer的实现类
//将实例添加到集合中
initializers.add(instantiateInitializer(contextClass, initializerClass));
}
//调用这些实例 跳转到步骤4
applyInitializers(context, initializers);
}
private void applyInitializers(ConfigurableApplicationContext context,
List<ApplicationContextInitializer<?>> initializers) {
//根据order属性进行排序
initializers.sort(new AnnotationAwareOrderComparator());
//循环刚刚的实例集合
for (ApplicationContextInitializer initializer : initializers) {
//调用ApplicationContextInitializer的接口强制实现的initialize方法
//完成ApplicationContextInitializer实现类的起作用
initializer.initialize(context);
}
}
总结
四、面试题总结