一、核心启动源码之initialize
1、整体代码
如下会new SpringApplication对象,这个操作会调用初始化方法,
![](https://i-blog.csdnimg.cn/blog_migrate/dcbfdb9a30436b10b82664c0edba529d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fa67e9d23c931da2a5246c231f10483f.png)
@SuppressWarnings({ "unchecked", "rawtypes" })
private void initialize(Object[] sources) {
//sources即我们的启动文件,如Starter.java
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
//确定是否是webEnvironment,后续会根据是否是web环境进行一些操作,如上下文初始化实例的选择(createApplicationContext)
this.webEnvironment = deduceWebEnvironment();
//上下文初始化:使用内部工具类SpringFactoriesLoader从META-INF/spring.factories加载加载所有ApplicationContextInitializer实现类并实例化,
//并将这些实例设置到SpringApplication的List<ApplicationContextInitializer<?>> initializers中
//初始化实例包含:
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//监听实例初始化:从META-INF/spring.factories加载实现了ApplicationListener接口的类并生成实例对象,
//并将这些实例设置到SpringApplication的private List<ApplicationListener<?>> listeners中
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//获取main函数所在的类
this.mainApplicationClass = deduceMainApplicationClass();
}
2、springboot - web环境的推断
SpringApplication会尝试帮你创建正确的ApplicationContext,默认情况下会使用AnnotationConfigApplicationContext或者 AnnotationConfigEmbeddedWebApplicationContext,这取决于你开发的是否是web环境。判断Web environment算法是非常简单的,直接基于某些类的存在与否。
如下:直接判断类路径下是否存在"javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext"两个类即可。
![](https://i-blog.csdnimg.cn/blog_migrate/cf070f9d6b2d5bdd9c102d19223cb223.png)
![](https://i-blog.csdnimg.cn/blog_migrate/eba5618ef2a3eecda9aa154f006f7d4f.png)
3、上下文初始化
![](https://i-blog.csdnimg.cn/blog_migrate/6920fd89e6f653dfe49bec5c8a787c0d.png)
上下文实例详解:
//缺省 Web SpringApplication 应用会有以下 6 个 ApplicationContextInitializer:
//1.初始化任务委托给Environment指定的初始化器,
//相当于给外界提供了一个添加自定义ApplicationContextInitializer的入口
//委托给Environment属性context.initializer.classes下指定的ApplicationContextInitializer类,
DelegatingApplicationContextInitializer
//2.设置Spring ApplicationContext ID
//这些环境变量会被用来产生Spring ApplicationContext ID :
// spring.application.name,vcap.application.name,spring.config.name
// 如果没有找到以上属性设置,ID使用 application
ContextIdApplicationContextInitializer
//3.用于报告一般配置错误,
//添加BeanFactoryPostProcessor : ConfigurationWarningsPostProcessor
ConfigurationWarningsApplicationContextInitializer
//4.添加 ApplicationListener<EmbeddedServletContainerInitializedEvent>
ServerPortInfoApplicationContextInitializer
//5.在ConfigurationClassPostProcessor和Spring boot之间增加一个共享的CachingMetadataReaderFactory
//添加BeanFactoryPostProcessor : CachingMetadataReaderFactoryPostProcessor
SharedMetadataReaderFactoryContextInitializer
//6.将ConditionEvaluationReport写到log,在DEBUG级别下输出
//添加AutoConfigurationReportListener
AutoConfigurationReportLoggingInitializer
4、监听初始化
![](https://i-blog.csdnimg.cn/blog_migrate/9d4c54b9b240f5fadeb0969ca901fcaa.png)
二、核心启动源码之run方法
1、整体代码
public ConfigurableApplicationContext run(String... args) {
//创建计时器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 声明 IOC 容器
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
//作用:即使没有检测到显示器,也允许其启动。对于服务器来说,是不需要显示器的,所以要这样设置
configureHeadlessProperty();
//准备运行时监听器EventPublishingRunListener,方式为从类路径下找到 META/INF/Spring.factories 获取 SpringApplicationRunListeners
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 封装命令行参数,也就是在命令行下启动应用带的参数,如--server.port=9000
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备环境,1、加载外部化配置的资源到environment;2、触发ApplicationEnvironmentPreparedEvent事件,
//创建环境完成后回调 SpringApplicationRunListeners#environmentPrepared()方法,表示环境准备完成
// 环境准备的主要工作就是 将系统属性配置及用户定义的属性配置 加载进来
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
// 打印banner图。banner即下面这坨东西,可以设置关掉打印,也可以修改
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.6.RELEASE)
Banner printedBanner = printBanner(environment);
//创建应用上下文,决定创建web的ioc还是普通的ioc
context = createApplicationContext();
//容器启动失败分析,如端口被占用等
analyzers = new FailureAnalyzers(context);
//做context的准备工作,如把相关需要创建bean的类加载到上下文中
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新上下文,
//很重要的就包含对上面被加载的bean进行实例化
//另外,@EnableAutoConfiguration相关配置也在这里将需要装配的bean给装配好
refreshContext(context);
//刷新Context容器之后处理
afterRefresh(context, applicationArguments);
//通过EventPublishingRunListener发布finished事件
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
2、SpringApplicationRunListeners
SpringApplicationRunListener 的作用是在SpringApplication 的各个启动过程中,监听各个阶段的变化,并将每个阶段封装成事件(ApplicationEvent),发布出去。让其他监听这些事件的监听器能探测到,并调用到对应的处理方法。
1. SpringApplicationRunListener 是一个接口,它的主要实现类是 EventPublishingRunListener 类。主要作用是监听 SpringApplication 启动的各个阶段,并封装成对应的 ApplicationEvent ,并把它发布出去。
2. 这里运用到一个观察者模式,不懂的小朋友可以多百度一下。
3. 我们可以定义自己的SpringApplicationRunListener, 实现这个接口,然后再新建一个 META-INF/spring.factorie 文件,注册监听即可。
3、SpringApplicationRunListeners与ApplicationListener的区别
SpringApplicationRunListeners负责在SpringBoot启动的不同阶段, 广播出不同的消息, 传递给ApplicationListener监听器实现类。
SpringApplicationRunListeners从Java监听器的角度看,并不是一个监听器,只是个普通的Java类而已,可以去看这个类的定义。
而ApplicationListener是传统意义上的Java监听器,如实现类DelegatingApplicationListener, 实现了ApplicationListener,ApplicationListener继承了EventListener。
4、ApplicationContextInitializer
用来在对ApplicationContext
进行refresh操作之前对Application context进行一些初始化操作。
主要用来在ConfigurableApplicationContext#refresh()
之前对ConfigurableApplicationContext
进行一些初始化的操作。一般来说主要做一些程序化的操作,比如注册配置源,根据配置激活某个profile等。
5、ApplicationListener
基于观察者模式的Application的事件监听器。将ApplicationListener
注册到ApplicationContext
中,当有对应事件发生时,监听器会被调用。
ApplicationContextInitializer
和ApplicationListener
的加载是在类SpringApplication
的构造函数中完成的,具体都是通过调用函数getSpringFactoriesInstances(Class<T> type)
来完成的。
在spring boot中默认配置的ApplicationListener
还挺多的,我们就摘抄几个比较重要的分析一下。
ConfigFileApplicationListener
类ConfigFileApplicationListener
是一个非常重要的监听器,除了是一个监听器之外,它实现了接口EnvironmentPostProcessor
。在监听到事件时,会从指定的文件加载配置并配置application context。
可以看到继承实现了接口EnvironmentPostProcessor
的方法postProcessEnvironment
,负责对SpringApplication的Environment进行处理。具体实现上可以看到创建了一个Loader,这个Loader会负责去javadoc描述得地方去查找文件并加载配置。
public class ConfigFileApplicationListener
implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
private void onApplicationEnvironmentPreparedEvent(
ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(),
event.getSpringApplication());
}
}
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
}
protected void addPropertySources(ConfigurableEnvironment environment,
ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
new Loader(environment, resourceLoader).load();
}
}
重点看一下方法onApplicationEnvironmentPreparedEvent
。在监听到ApplicationEnvironmentPreparedEvent事件之后,除了将自己加入到List<EnvironmentPostProcessor>
之外,还会调用函数loadPostProcessors
去加载在文件META-INF/spring.factories配置的EnvironmentPostProcessor
,最后执行所有的postProcessEnvironment
方法。
三、EnableAutoConfiguration
@EnableAutoConfiguration结合spring.factories实现了对外部依赖包的扫描。
具体参考我的另一篇博客:总结:Spring boot之@EnableAutoConfiguration
参考:
总结:Spring Boot 之spring.factories
Spring Boot 中 @EnableXXX 注解的驱动逻辑
SpringApplicationRunListener 是干啥的?
spring-boot-2.0.3不一样系列之源码篇 - run方法(三)之createApplicationContext,绝对有值得你看的地方