关联文章:
SpringBoot源码解析三部曲(一)——自动配置
SpringBoot源码解析三部曲(二)——SpringApplication实例化
目录
1、SpringApplication run方法简介
在上一篇已经介绍了,当SpringApplication对象被创建之后,通过调用其run方法进行Spring Boot的启动和运行,正式开启SpringApplication的生命周期。
SpringApplication调用的run方法的核心操作如下图所示:
对照流程图,来整体看一下run方法的源码:
public ConfigurableApplicationContext run(String... args) {
// 创建StopWatch对象, 用于统计run方法启动时长
StopWatch stopWatch = new StopWatch();
// 启动统计
stopWatch.start();
// 应用上下文对象,Spring Boot使用的应用上下文是ConfigurableApplicationContext
ConfigurableApplicationContext context = null;
// 异常报告器
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 配置headless属性
configureHeadlessProperty();
/**
* 获得SpringApplicationRunListener数组, 并将其封装在变量listeners中
* SpringApplicationRunListener接口是监听SpringApplication中run方法各个执行阶段的, 提供了一系列的方法, 用户可以通过回调这些方法, 在启动各个阶段时加入指定的逻辑处理
*/
SpringApplicationRunListeners listeners = getRunListeners(args);
// 启动监听器, 里面的操作为遍历SpringApplicationRunListener数组每个元素, 并执行
listeners.starting();
try {
// 初始化ApplicationArguments对象, ApplicationArguments是用于提供访问运行SpringApplication时的参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 初始化ConfigurableEnvironment, 加载属性配置, 包括所有的配置属性(如: application.properties中和外部的属性配置)
// ConfigurableEnvironment接口的主要作用是提供当前运行环境的公开接口, 比如配置文件profiles各类系统属性和变量的设置, 添加, 读取, 合并
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 对环境中忽略信息配置项spring.beaninfo.ignore的值进行判断, 如果为true, 跳过BeanInfo类的扫描
configureIgnoreBeanInfo(environment);
// 打印Bannner
Banner printedBanner = printBanner(environment);
// 创建容器
context = createApplicationContext();
// 获取异常报告器
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 准备容器, 将组件对象之间进行关联
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新容器
refreshContext(context);
// 初始化操作之后执行, 默认实现为空
afterRefresh(context, applicationArguments);
// 停止时长统计
stopWatch.stop();
// 打印启动日志
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 通知监听器: 容器启动完成
listeners.started(context);
// 调用ApplicationRunner和CommandLineRunner的运行方法, 用来给用户实现一些在容器启动时的自定义操作
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;
}
2、SpringApplicationRunListener监听器
2.1、SpringApplicationRunListeners容器
从上述源码中可以看到,除了计时统计的功能,第一步就是监听器SpringApplicationRunListeners的获取和使用。SpringApplicationRunListeners是一个SpringApplicationRunListener的容器,它将SpringApplicationRunListener的集合以构造方法传入,并赋值给其成员变量listeners,然后提供了针对listeners成员变量的各种遍历操作方法。源码如下:
class SpringApplicationRunListeners {
private final Log log;
private final List<SpringApplicationRunListener> listeners;
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
void contextPrepared(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextPrepared(context);
}
}
void contextLoaded(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextLoaded(context);
}
}
void started(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
void running(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.running(context);
}
}
void failed(ConfigurableApplicationContext context, Throwable exception) {
for (SpringApplicationRunListener listener : this.listeners) {
callFailedListener(listener, context, exception);
}
}
}
SpringApplicationRunListeners的构建很简单,run方法中使用的是getRunListeners私有方法,源码如下:
private SpringApplicationRunListeners getRunListeners(String[] args) {
// 构造Class数组
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
// 调用SpringApplicationRunListeners的构造方法
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
/**
* 用来获取spring.factories配置文件中的注册类, 并进行实例化操作
*/
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
// 获取类加载器
ClassLoader classLoader = getClassLoader();
// 加载META-INF/spring.factories中对应的配置, 并将结果存储于Set中, 方便去重
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 创建实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
/**
* 实例化注册类
*/
@SuppressWarnings("unchecked")
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对象
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;
}
getSpringFactoriesInstances方法在前两篇也介绍了很多次了,通过SpringFactoriesLoader的loadFactoryNames方法加载META-INF/spring.factories中对应的配置。SpringApplicationRunListener的配置只有一个——EventPublishingRunListener。
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
2.2、SpringApplicationRunListener解析
接口SpringApplicationRunListener是SpringApplication的run方法监听器,提供了一系列的方法,用户可以通过回调这些方法,在启动的各个流程加入指定的逻辑处理。源码如下
public interface SpringApplicationRunListener {
// 当run方法第一次被执行时, 会被立即调用, 可用于非常早期的初始化工作
default void starting() {
}
// 当environment准备完成, 在ApplicationContext容器创建之前, 该方法被调用
default void environmentPrepared(ConfigurableEnvironment environment) {
}
// 当ApplicationContext构建完成, 资源还未被加载时, 该方法被调用
default void contextPrepared(ConfigurableApplicationContext context) {
}
// 当ApplicationContext资源加载完成, 未被刷新之前, 该方法被调用
default void contextLoaded(ConfigurableApplicationContext context) {
}
// 当ApplicationContext刷新并启动之后, CommandLineRunner和ApplicationRunner未被调用之前, 该方法被调用
default void started(ConfigurableApplicationContext context) {
}
// 当所有准备工作就绪, run方法执行完成之前, 该方法被调用
default void running(ConfigurableApplicationContext context) {
}
// 当应用程序出现错误时, 该方法被调用
default void failed(ConfigurableApplicationContext context, Throwable exception) {
}
}
从源码中可以看出,接口SpringApplicationRunListener为run方法提供了各个运行阶段的监听事件处理功能,执行时机如下图所示:
2.2.1、实现类EventPublishingRunListener解析
从META-INF/spring.factories配置文件中可以看出,SpringApplicationRunListener只有一个默认实现类EventPublishingRunListener,EventPublishingRunListener使用内置的SimpleApplicationEventMulticaster来广播在应用上下文刷新之前触发的事件。
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;
// 事件广播器
private final SimpleApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
// 创建并设置广播器
this.initialMulticaster = new SimpleApplicationEventMulticaster();
// 遍历ApplicationListener,并关联SimpleApplicationEventMulticaster广播器
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
// 当run方法第一次被执行时,该方法被调用
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
// 当environment准备完成,在ApplicationContext容器创建之前,该方法被调用
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster
.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
// 当ApplicationContext容器构建完成,资源还未被加载时,该方法被调用
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
this.initialMulticaster
.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}
// 当ApplicationContext容器资源加载完成,未被刷新之前,该方法被调用
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
// 遍历application中所有的监听器
for (ApplicationListener<?> listener : this.application.getListeners()) {
// 如果为ApplicationContextAware, 则将上下文信息设置到该监听器内
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
// 将application中的监听器全部添加到上下文中
context.addApplicationListener(listener);
}
// 广播事件ApplicationPreparedEvent
this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}
// 当ApplicationContext容器刷新并启动后,CommandLineRunner和ApplicationRunner未被调用之前,该方法被调用
@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
}
// 当所有准备工作就绪,run方法执行完成之前,该方法被调用
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
}
// 当应用程序出现错误时, 该方法被调用
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
if (context != null && context.isActive()) {
context.publishEvent(event);
}
else {
if (context instanceof AbstractApplicationContext) {
for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
.getApplicationListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
this.initialMulticaster.multicastEvent(event);
}
}
}
Spring Boot完成基本的初始化之后,会遍历SpringApplication的所有ApplicationListener实例,并将它们与SimpleApplicationEventMulticaster进行关联,方便SimpleApplicationEventMulticaster后续将事件传递给所有的监听器。
EventPublishingRunListener针对不同的事件的处理流程基本相同,可以概括为以下几个步骤:
- 当run方法运行到某个阶段时,调用EventPublishingRunListener的某个方法。
- EventPublishingRunListener的某个方法将application参数和args参数封装到对应的事件中,这里的事件均为SpringApplicationEvent的实现类。
- 通过成员变量initialMulticaster的multicastEvent方法对事件进行广播,或者通过该方法参数ConfigurableApplicationContext的publishEvent来对事件进行发布。
- 对应的ApplicationListener被触发,执行响应的业务逻辑。
从上述步骤中可以看出,有些方法时通过成员变量initialMulticaster的multicastEvent方法对事件进行广播,有些方法是通过参数ConfigurableApplicationContext的publishEvent来对事件进行发布。从这些方法中可以找到contextLoaded这个方法,它是两种不同事件广播形式的分水岭。contextLoaded方法在发布事件之前做了两件事:一,遍历application的所有监听器实现类,如果该实现类还实现了ApplicationContextAware接口,则将上下文信息设置到该监听器内;二、将application中的监听器实现类全部添加到上下文中。最后调用事件广播。在contextLoaded方法执行之前,上下文还没有初始化完成,所以无法通过它的publishEvent方法进行事件发布。
3、初始化ApplicationArguments
在run方法源码中,监听器启动之后,紧接着便是执行ApplicationArguments对象的初始化,ApplicationArguments是用于提供访问运行SpringApplication时的参数。
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
public DefaultApplicationArguments(String... args) {
Assert.notNull(args, "Args must not be null");
this.source = new Source(args);
this.args = args;
}
在DefaultApplicationArguments将参数args封装为Source对象,Source对象是基于Spring框架的SimpleCommandLinePropertySource来实现的。
4、初始化ConfigurableEnvironment
完成ApplicationArguments参数的准备之后,便开始通过prepareEnvironment方法对ConfigurableEnvironment对象进行初始化操作。ConfigurableEnvironment接口的主要作用是提供当前运行环境的公开接口,比如配置文件profiles各类系统属性和变量的设置、添加、读取、合并等功能。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 获取或创建环境
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置环境, 主要包括PropertySource和activeProfiles的配置
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 将ConfigurationPropertySources附加到指定环境中的第一位, 并动态跟踪环境的添加或删除
ConfigurationPropertySources.attach(environment);
// 通知监听器listener环境准备完成
listeners.environmentPrepared(environment);
// 将环境绑定到SpringApplication, 将环境绑定到name为spring.main的目标上
bindToSpringApplication(environment);
// 判断是否定制的环境, 如果不是定制的则将环境转换到StandardEnvironment
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
// 再次将ConfigurationPropertySources附加到指定环境中的第一位, 并动态跟踪环境的添加或删除
ConfigurationPropertySources.attach(environment);
return environment;
}
4.1、获取或创建环境
该方法比较简单,如果environment存在,则直接返回。如果environment不存在,则根据SpringApplication实例化时推断的webApplicationType来进行区分创建环境。
private ConfigurableEnvironment getOrCreateEnvironment() {
// 如果environment存在, 则直接返回
if (this.environment != null) {
return this.environment;
}
// 根据前面推断的webApplicationType, 创建不同的环境实现
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
4.2、配置环境
在获得环境变量对象之后,开始对环境变量和参数进行相应的设置,主要包括转换服务的设置、PropertySources的设置和activeProfiles的设置。
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
// 如果为true则获取并设置转换服务
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
// 配置PropertySources
configurePropertySources(environment, args);
// 配置Profiles
configureProfiles(environment, args);
}
首先判断addConversionService变量是否为true,也就是判断是否需要添加转换服务,如果需要,则获取转换服务实例,并对环境设置转换服务。随后进行PropertySources和Profiles的配置。
// 配置PropertySources
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
// 获取环境中的属性资源信息
MutablePropertySources sources = environment.getPropertySources();
// 如果默认属性配置存在则将其放置于属性资源的最后位置
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}
// 如果命令行属性存在
if (this.addCommandLineProperties && args.length > 0) {
// 常量值为:commandLineArgs
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
// 如果默认属性资源中不包含该命令,则将命令行属性放置在第一位,如果包含,则通过CommandLinePropertySource进行处理
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(
new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
// 放置在第一位
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
主要是对参数的优先级进行处理,首先,如果存在默认属性配置,则将默认属性配置放置在最后,也就是优先级最低。对于命令参数,如果命令参数已经存在于属性配置中,则使用CompositePropertySource类进行相同name的参数处理;如果命令参数并不存在于属性配置中,则直接将其设置为优先级最高。
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
// 如果存在额外的Profiles,则将其放置在第一位,随后再获得其他的Profiles
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
5、忽略信息配置
在上述完成ConfigurableEnvironment的初始化之后,程序又对环境中的忽略信息配置项(参数spring.beaninfo.ignore)的值进行判断,进而设置为系统参数中的忽略项。
private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
// 如果系统参数中spring.beaninfo.ignore为null
if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
// 获取环境中spring.beaninfo.ignore的数量
Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
// 设置对应的系统参数
System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
}
}
spring.beaninfo.ignore的配置用来决定是否跳过BeanInfo类的扫描,如果设置为true,则跳过。
6、打印Banner
完成环境的基本处理之后,下面就是控制台Banner的打印了。
private Banner printBanner(ConfigurableEnvironment environment) {
// 如果处于关闭状态,则返回null
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
// 如果resourceLoader不存在则创建一个默认的ResourceLoader
ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
: new DefaultResourceLoader(getClassLoader());
// 创建SpringApplicationBannerPrinter
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
// 打印到日志中
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
// 打印到控制台
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
7、创建应用上下文
根据推断的Web应用类型,创建对应的上下文。
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {
// 获取容器中的类变量
Class<?> contextClass = this.applicationContextClass;
// 如果为null, 则根据Web应用类型按照默认类进行创建
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
// 如果存在对应的Class配置,则通过Spring提供的BeanUtils来进行实例化
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
8、准备应用上下文
完成了应用上下文的创建,SpringApplication通过prepareContext方法来进行应用上下文的准备,核心功能和流程如下图所示:
结合流程图,看一下prepareContext方法的源码:
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// --------------------- 应用上下文准备阶段 ---------------------
// 设置上下文的配置环境
context.setEnvironment(environment);
// 应用上下文后置处理
postProcessApplicationContext(context);
// 在上下文刷新之前, ApplicationContextInitializer初始化上下文
applyInitializers(context);
// 通知监听器上下文准备完成
listeners.contextPrepared(context);
// --------------------- 应用上下文加载阶段 ---------------------
// 打印日志, 启动Profile
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 获取ConfigurableListableBeanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 将beanFactory注册为单例对象
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());
}
// 获取全部配置源, 其中包含primarySources(被@SpringBootApplication注解或@EnableAutoConfiguration注解注释的类)和sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 将配置源中的Bean加载到上下文中
load(context, sources.toArray(new Object[0]));
// 通知监听器上下文加载完成
listeners.contextLoaded(context);
}
从代码中可以看出,准备应用上下文分为两个阶段,准备阶段、加载阶段,下面依次来看下这两个阶段。
8.1、应用上下文准备阶段
在上下文准备阶段,主要有3步操作:对应用上下文Context设置environment、应用上下文后置处理、ApplicationContextInitializer初始化Context。
8.1.1、对应用上下文Context设置environment
public void setEnvironment(ConfigurableEnvironment environment) {
// 设置Context的environment
super.setEnvironment(environment);
// 设置Context的reader属性的conditionEvaluator属性
this.reader.setEnvironment(environment);
// 设置Context的scanner属性的environment属性
this.scanner.setEnvironment(environment);
}
8.1.2、应用上下文后置处理
设置beanNameGenerator、resourceLoader、classLoader、conversionService,在此阶段,beanNameGenerator和resourceLoader都为null,实际上只做了最后一步的设置转换服务。
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
// 如果beanNameGenerator不为null, 则将当前的beanNameGenerator按照默认名字进行注册
context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
// 如果resourceLoader不为null, 则根据上下文的类型分别进行ResourceLoader和ClassLoader的设置
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
}
}
// 如果为true则获取并设置转换服务
if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
8.1.3、对应用上下文初始化
protected void applyInitializers(ConfigurableApplicationContext context) {
// 获取ApplicationContextInitializer集合并遍历
for (ApplicationContextInitializer initializer : getInitializers()) {
// 解析当前遍历的initializer实现ApplicationContextInitializer的泛型类型
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
// 判断泛型是否和ConfigurableApplicationContext匹配
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
// 初始化上下文
initializer.initialize(context);
}
}
8.2、应用上下文加载阶段
在上下文加载阶段,主要有5步操作:打印日志和Profile的设置、设置是否允许覆盖注册、获取全部配置源、将配置源加载到上下文、通知监听器Context加载完成。
获取ConfigurableListableBeanFactory并注册单例对象,注册的单例对象包含:ApplicationArguments和Banner。当BeanFactory为DefaultListableBeanFactory时,进入设置是否允许覆盖注册的处理逻辑。当ApplicationArguments类单例对象注册之后,也就意味着我们在使用Spring应用上下文的过程中可以通过依赖注入来使用该对象。
8.2.1、获取全部配置源
public Set<Object> getAllSources() {
Set<Object> allSources = new LinkedHashSet<>();
// 如果primarySources不为空,则将其添加到Set集合中
// primarySources为被@SpringBootApplication注解或@EnableAutoConfiguration注解修饰的类
if (!CollectionUtils.isEmpty(this.primarySources)) {
allSources.addAll(this.primarySources);
}
// 如果sources不为空,则将其添加到Set集合中
if (!CollectionUtils.isEmpty(this.sources)) {
allSources.addAll(this.sources);
}
return Collections.unmodifiableSet(allSources);
}
8.2.2、加载配置源
在获取到所有配置源之后,通过load方法将配置源信息加载到上下文中。
protected void load(ApplicationContext context, Object[] sources) {
// 日志打印
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
// 创建BeanDefinitionLoader,通过BeanDefinitionLoader来完成配置资源的加载操作
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
// 调用BeanDefinitionLoader的load方法
loader.load();
}
// BeanDefinitionLoader构造方法
// 可以看出BeanDefinitionLoader支持基于AnnotatedBeanDefinitionReader、XmlBeanDefinitionReader、GroovyBeanDefinitionReader等多种类型的加载操作。
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
Assert.notNull(registry, "Registry must not be null");
Assert.notEmpty(sources, "Sources must not be empty");
this.sources = sources;
this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
this.xmlReader = new XmlBeanDefinitionReader(registry);
if (isGroovyPresent()) {
this.groovyReader = new GroovyBeanDefinitionReader(registry);
}
this.scanner = new ClassPathBeanDefinitionScanner(registry);
this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}
// BeanDefinitionLoader中的load方法,加载支持的范围包括:Class、Resource、Package、CharSequence
private int load(Object source) {
Assert.notNull(source, "Source must not be null");
if (source instanceof Class<?>) {
return load((Class<?>) source);
}
if (source instanceof Resource) {
return load((Resource) source);
}
if (source instanceof Package) {
return load((Package) source);
}
if (source instanceof CharSequence) {
return load((CharSequence) source);
}
throw new IllegalArgumentException("Invalid source type " + source.getClass());
}
9、刷新应用上下文
应用上下文准备完成之后,便开始对应用上下文进行刷新。
private void refreshContext(ConfigurableApplicationContext context) {
// 调用refresh方法
refresh(context);
if (this.registerShutdownHook) {
try {
// 注册shutdownHook线程, 实现销毁时的回调
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
// 调用spring中的refresh方法, 在refresh中实现对@EnableAutoConfiguration注解的自动配置
((AbstractApplicationContext) applicationContext).refresh();
}
最后是通过Spring中的AbstractApplicationContext类的refresh进行刷新,这就属于Spring的内容了,可以简单看下。
public void refresh() throws BeansException, IllegalStateException {
// 整个通过同步处理
synchronized (this.startupShutdownMonitor) {
// 准备刷新工作
prepareRefresh();
// 通知子类刷新并获取BeanFactory, 这个获取到的是DefaultListableBeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 为当前上下文Context准备bean工厂
prepareBeanFactory(beanFactory);
try {
// 设置BeanFactory的后置处理器
postProcessBeanFactory(beanFactory);
// 调用BeanFactory的后置处理器, 这些后置处理器是在Bean定义中向容器注册的
invokeBeanFactoryPostProcessors(beanFactory);
// 注册Bean的后置处理器, 在Bean创建过程中调用
registerBeanPostProcessors(beanFactory);
// 对上下文的消息源进行初始化
initMessageSource();
// 初始化上下文中的事件机制
initApplicationEventMulticaster();
// 初始化其他的特殊Bean
onRefresh();
// 检查监听Bean并且将这些Bean向容器注册
registerListeners();
// 实例化所有非懒加载的单例Bean
finishBeanFactoryInitialization(beanFactory);
// 发布容器事件, 结束刷新过程
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
resetCommonCaches();
}
}
}
10、调用ApplicationRunner和CommandLineRunner
应用上下文刷新完成之后,调用ApplicationRunner和CommandLineRunner的运行方法, 用来给用户实现一些在容器启动时的自定义操作。
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
// 从应用上下文context中获得类型为ApplicationRunner的Bean,并将其加入到集合中
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// 从应用上下文context中获得类型为CommandLineRunner的Bean,并将其加入到集合中
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// 排序
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
// 调用callRunner方法
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
try {
// 执行其run方法
(runner).run(args);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
}
}