前言
本文是作者写关于Spring源码的第一篇文章,作者水平有限,所有的源码文章仅限用作个人学习记录。文中如有错误欢迎各位留言指正。
上一篇文章分析到Spring Boot启动的时候初始化了ApplicationContext容器。今天接着看容器初始化了后,对容器对象做了哪些操作。
设置ApplicationsStartUp对象
java
public ConfigurableApplicationContext run(String... args) {
// 为了计时用的,老版本和新版本不一样
long startTime = System.nanoTime();
// 初始化一个引导器的上下文,这是属于Spring Boot的上下文。后边还有一个Spring的上下文。apach好喜欢context这个东西,证明写框架这个context是真的好用。
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// 这是Spring的上下文,在这里定义,在下面进行的初始化
ConfigurableApplicationContext context = null;
// 配置一个系统属性
configureHeadlessProperty();
// 获取配置文件的监听器 重点 也是扩展点,凡是读取配置文件的地方都是扩展点,因为配置在配置文件中的initializer、listener都会在某个阶段被调用
SpringApplicationRunListeners listeners = getRunListeners(args);
// 调用监听器发送启动事件,这里可以通过自定义监听器消费这个事件,处理自己的逻辑
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 解析命令行参数 将其封装成一个ApplicationArguments,这个类的变量name被设置成commandLineArgs字符串,变量source是解析args封装的CommandLineArgs对象。
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 环境预处理
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 配置忽略beanInfo
configureIgnoreBeanInfo(environment);
// 打印banner信息
Banner printedBanner = printBanner(environment);
// 创建ApplicationContext容器
context = createApplicationContext();
// 给上下文设置应用启动对象
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
setApplicationStartup
applicationStartup是一个应用启动对象。该函数的作用是将应用启动对象与上下文对象关联起来,以便在上下文对象中可以访问应用启动对象的相关属性和方法。
prepareContext
传入的参数绑定到特定的上下文对象中,为应用程序的启动和运行做好准备。
主要做了以下操作:
-
将bootstrapContext绑定到context中,以便在应用程序中访问引导上下文信息。
-
将environment绑定到context中,以便在应用程序中访问环境变量和属性。
-
将listeners绑定到context中,以便在应用程序启动和停止时触发相应的监听器事件。
-
将applicationArguments绑定到context中,以便在应用程序中访问命令行参数。
-
将printedBanner绑定到context中,以便在应用程序启动时控制是否打印欢迎信息。
通过执行这些操作,prepareContext函数为应用程序的上下文环境准备了必要的信息和配置,使得应用程序能够正常启动和运行。
我们进入到方法看一下。
java
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 将当前的环境变量赋值给context的environment属性,所有在后续的操作中,可以通过context对象获取到当前的环境变量信息
context.setEnvironment(environment);
// 给容器中添加一些bean信息
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
bootstrapContext.close(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 AbstractAutowireCapableBeanFactory) {
((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
// 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);
}
postProcessApplicationContext
主要用于在Spring应用程序上下文初始化后,根据配置进行一些额外的设置和配置。
-
如果设置了beanNameGenerator,则将其注册为一个单例bean,用于生成bean的名称。
-
如果设置了resourceLoader,则根据上下文类型的不同,分别设置上下文的资源加载器或类加载器。
-
如果addConversionService为true,则将环境的转换服务设置为bean工厂的转换服务。
java
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
context.getBeanFactory()
.registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this.beanNameGenerator);
}
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
}
}
if (this.addConversionService) {
context.getBeanFactory().setConversionService(context.getEnvironment().getConversionService());
}
}
applyInitializers
依次调用获取到的ApplicationContextInitializer类型的实例来初始化ConfigurableApplicationContext上下文。具体实现是通过for-each循环遍历getInitializers()方法返回的initializer数组,然后通过GenericTypeResolver.resolveTypeArgument()方法获取initializer的实际类型,再使用Assert.isInstanceOf()方法判断context是否是该类型,最后调用initializer.initialize(context)方法来初始化上下文。
java
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
OK 今天先到这里吧。
See you next time :)