一、简介
上一篇说了SpringApplication的创建,这一篇说run方法,关于IOC容器、BeanFactory、ServletContainer的创建和启动、Bean的注册和实例化都在run方法中。
调试源码的时候注意:
ApplicationContext为常说的Spring容器、IOC容器、应用上下文、Spring上下文。
ApplicationContext中有BeanFactory。
注意BeanPostProcessor和BeanFactoryPostProcessor的区别!
debug调式SpringBoot 1.5.8的源码,运行在tomcat上。
Spring Boot 通过扫描classpath下的实例决定装载哪个web容器。
我这里pom.xml文件用的spring-boot-starter-web所以,会启动tomcat来作为servlet的容器。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
整个启动流程图:
二、SpringApplication的实例创建好后调用run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
//开始计时(初始化需要花费多少时间)
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
//配置headless
configureHeadlessProperty();
//实现类只有EventPublishingRunListener
//通过SpringApplicationRunListeners广播
SpringApplicationRunListeners listeners = getRunListeners(args);
//收到广播的类执行相应的操作
listeners.starting();
try {
// 构造一个应用程序参数持有类
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 配置环境模,这里面ConfigFileApplicationListener接收到事件后会调用postProcessEnvironment方法加载application.yml配置文件
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//配置Banner
Banner printedBanner = printBanner(environment);
// 创建Spring的ApplicationContext
//class org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
//这里面会执行所有的初始化器Initializer的逻辑
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//执行bean的创建和实例化
refreshContext(context);
afterRefresh(context, applicationArguments);
// 广播出ApplicationReadyEvent事件给相应的监听器执行
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);
}
}
run方法主要做的事有:
1.stopWatch.start()
开始计时初始化需要花费多少时间
源码
public void start(String taskName) throws IllegalStateException {
if (this.running) {
throw new IllegalStateException("Can't start StopWatch: it's already running");
}
this.running = true;
this.currentTaskName = taskName;
this.startTimeMillis = System.currentTimeMillis();
}
2.Headless配置
configureHeadlessProperty();
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
3.创建SpringApplicationRunListeners,发送事件广播
SpringApplicationRunListener目前只有一个实现类EventPublishingRunListener。
这里listeners.starting()发送了ApplicationStartedEvent广播。
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
SpringApplicationRunListenesr类有5个方法,发送了不同的事件:
方法 | 何时调用 | 对应事件 |
starting | run方法执行时就调用 | ApplicationStartedEvent |
environmentPrepared | ApplicationContext创建之前并且环境信息准备好的时候调用 | ApplicationEnvironmentPreparedEvent |
contextPrepared | ApplicationContext创建好并且在source加载之前调用一次 | 无,实现类EventPublishingRunListener中该方法为空的 |
contextLoaded | ApplicationContext创建并加载之后并在refresh之前调用 | ApplicationPreparedEvent |
finished | run方法结束之前调用 | ApplicationReadyEvent或ApplicationFailedEvent |
监听器收到 ApplicationStartedEvent事件广播后执行的逻辑:
3.1 LoggingApplicationListener处理ApplicationStartedEvent事件
源码
private void onApplicationStartingEvent(ApplicationStartingEvent event) {
this.loggingSystem = LoggingSystem
.get(event.getSpringApplication().getClassLoader());
this.loggingSystem.beforeInitialize();
}
初始化日志系统
3.2 BackgroundPreinitializer处理ApplicationStartedEvent事件
无,该监听器并没有监听该事件
3.3 DelegatingApplicationListener处理ApplicationStartedEvent事件
无,没有监听该事件
3.4 LiquibaseServiceLocatorApplicationListener处理ApplicationStartedEvent事件
源码
public void onApplicationEvent(ApplicationStartingEvent event) {
if (ClassUtils.isPresent("liquibase.servicelocator.ServiceLocator", null)) {
new LiquibasePresent().replaceServiceLocator();
}
}
无,并没有走到判断条件中
4.配置环境模块prepareEnvironment
配置一些环境信息。
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
源码
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 创建应用程序的环境信息。如果是web程序,创建StandardServletEnvironment;否则,创建StandardEnvironment
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置一些环境信息。比如profile,命令行参数
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 广播出ApplicationEnvironmentPreparedEvent事件给相应的监听器执行
listeners.environmentPrepared(environment);
// 环境信息的校对 这里是true,并不会走if里面
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
return environment;
}
做了4件事
4.1创建应用程序的环境信息。
如果是web程序,创建StandardServletEnvironment;否则,创建StandardEnvironment。
4.2配置一些环境信息。比如profile,命令行参数。
4.3广播出ApplicationEnvironmentPreparedEvent事件给相应的监听器
监听器收到 ApplicationEnvironmentPreparedEvent事件广播后执行的逻辑:
4.3.1 ConfigFileApplicationListener处理ApplicationEnvironmentPreparedEvent事件
源码
private void onApplicationEnvironmentPreparedEvent(
ApplicationEnvironmentPreparedEvent event) {
// 1.读取spring.factories文件,加载环境后置处理器
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
// 2.把ConfigFileApplicationListener监听器也加入到环境后置处理器中
postProcessors.add(this);
// 3.排序
AnnotationAwareOrderComparator.sort(postProcessors);
// 4.执行每个PostProcessors后置处理环境的逻辑
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(),
event.getSpringApplication());
}
}
做了4件事
1.读取spring.factories文件,加载环境后置处理器(SpringApplicationJsonEnvironmentPostProcessor、CloudFoundryVcapEnvironmentPostProcessor)
2.把ConfigFileApplicationListener监听器也加入到环境后置处理器中
3.排序
4.执行每个PostProcessors后置处理环境的逻辑
其中,后置处理器及其执行逻辑:
SpringApplicationJsonEnvironmentPostProcessor源码:并没有走到if里面去,相当于什么都没做。
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
String json = environment.resolvePlaceholders(
"${spring.application.json:${SPRING_APPLICATION_JSON:}}");
if (StringUtils.hasText(json)) {
processJson(environment, json);
}
}
CloudFoundryVcapEnvironmentPostProcessor源码:并没有走到if里面去,相当于什么都没做
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
if (CloudPlatform.CLOUD_FOUNDRY.isActive(environment)) {
Properties properties = new Properties();
addWithPrefix(properties, getPropertiesFromApplication(environment),
"vcap.application.");
addWithPrefix(properties, getPropertiesFromServices(environment),
"vcap.services.");
MutablePropertySources propertySources = environment.getPropertySources();
if (propertySources.contains(
CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME)) {
propertySources.addAfter(
CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME,
new PropertiesPropertySource("vcap", properties));
}
else {
propertySources
.addFirst(new PropertiesPropertySource("vcap", properties));
}
}
}
ConfigFileApplicationListener源码:
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
configureIgnoreBeanInfo(environment);
bindToSpringApplication(environment, application);
}
关于ConfigFileApplicationListener,考虑另外专门写一篇来介绍
【九】Spring Boot 源码分析之启动主流程----ConfigFileApplicationListener的postProcessEnvironment方法
4.4环境信息的校对
源码:这里是true,并不会走if里面。
// 环境信息的校对 这里是true,并不会走if里面
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
5.配置Banner
是否在控制台上打印自定义的banner
Banner printedBanner = printBanner(environment);
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader
: new DefaultResourceLoader(getClassLoader());
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);
}
控制台的这个图像就是打印出来的Banner
6.创建spring容器 ApplicationContext
这里实际创建的容器是AnnotationConfigEmbeddedWebApplicationContext。
WebApplicationContext有两个核心实现,一个就是AnnotationConfigEmbeddedWebApplicationContext,用@Configuration配置方式装载类。另一个是XmlWebApplicationContext,用XML配置文件方式装载类
context = createApplicationContext();
源码
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 如果是web程序,那么构造org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext容器
// 否则构造org.springframework.context.annotation.AnnotationConfigApplicationContext容器
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
如果是web程序,那么构造org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext容器 ,否则构造org.springframework.context.annotation.AnnotationConfigApplicationContext容器。
在BeanUtils.instantiate方法中用反射创建ApplicationContext的时候会把容器需要的BeanFactory也一起创建了。
7.为刷新spring容器做准备
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
源码
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 设置Spring容器的环境信息
context.setEnvironment(environment);
// 回调方法,Spring容器创建之后做一些额外的事
postProcessApplicationContext(context);
// SpringApplication的的初始化器开始工作
applyInitializers(context);
// 遍历调用SpringApplicationRunListener的contextPrepared方法。目前只是将这个事件广播器注册到Spring容器中
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 把应用程序参数持有类注册到Spring容器中,并且是一个单例
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
// 广播出ApplicationPreparedEvent事件给相应的监听器执行
listeners.contextLoaded(context);
}
专门另外写一篇来详细介绍 SpringApplication类的prepareContext方法
【三】Spring Boot 源码分析之启动主流程---SpringApplication的prepareContext方法
8.刷新spring容器 ApplicationContext
refreshContext(context);
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
里面最终调用的是AbstractApplicationContext类的refresh方法来刷新容器
源码:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//设置Spring容器的启动时间,撤销关闭状态,开启活跃状态。
//初始化属性源信息
//验证环境信息里一些必须存在的属性
prepareRefresh();
//调用子类的refeshBeanFactory(),SpringBoot中采用默认的实现,设置BeanFactory的SerializationId,设置refreshed标志为true。
//获取BeanFactory
//XmlWebApplicationContext ,AnnotationConfigApplicationContext 会在这一步加载BeanDefinition
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//设置classloader用于加载bean,设置表达式解析器,设置资源编辑注册器
//添加ApplicationContextAwareProcessor这个BeanPostProcessor。取消ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware、EnvironmentAware这5个接口的自动注入。因为ApplicationContextAwareProcessor把这5个接口的实现工作做了
//注册依赖,如一个bean的属性中含有ApplicationEventPublisher(beanFactory),则会将beanFactory的实例注入进去。ResourceLoader、ApplicationEventPublisher、ApplicationContext这3个接口对应的bean都设置为当前的Spring容器
//注入一些其它信息的bean,比如environment、systemProperties等
prepareBeanFactory(beanFactory);
try {
//给beanFactory添加后置处理器WebApplicationContextServletContextAwareProcessor
postProcessBeanFactory(beanFactory);
//激活各种BeanFactory处理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor
//把BeanDefinition都注册到容器的注册表(一个Map)中,先注册PriorityOrdered,再注册Ordered,再注册nonOrdered的
invokeBeanFactoryPostProcessors(beanFactory);
//注册拦截Bean创建的Bean处理器,即注册BeanPostProcessor,不是BeanFactoryPostProcessor。
registerBeanPostProcessors(beanFactory);
//初始化上下文中的资源文件,如国际化文件的处理等
initMessageSource();
//在Spring容器中初始化事件广播器,事件广播器用于事件的发布。
initApplicationEventMulticaster();
// 该方法中会去创建内嵌的Servlet容器,这里既是Tomcat。
//跟到里面会发现启动了Tomcat的Server、Service、Engine、Realm、Pipeline、Value、MapperListerner
onRefresh();
//把Spring容器内的时间监听器和BeanFactory中的时间监听器都添加的事件广播器中,并且广播出去。
registerListeners();
// 实例化BeanFactory中已经被注册但是未实例化的所有实例(懒加载的不需要实例化)。即生成环境所需要的Bean。
finishBeanFactoryInitialization(beanFactory);
// 初始化生命周期处理器DefaultLifecycleProcessor,DefaultLifecycleProcessor含有start方法和stop方法
// 启动Tomcat中所有实现Lifecycle接口的组件
//通过spring的事件发布机制发布ContextRefreshedEvent事件。
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
专门另外写一篇来详细介绍 AbstractApplicationContext类的refresh方法
【四】Spring Boot 源码分析之启动主流程---AbstractApplicationContext的refresh方法
9. afterRefresh方法
实际上是调用了SpringApplication类的callRunners()方法
源码:
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<Object>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<Object>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
从ApplicationContext容器中拿出ApplicationRunner和CommandLineRunner的bean,调用他们每个的run方法。
这里debug的时候由于没有Runner,就直接跳过了。
10.广播
listeners.finished(context, null);
这里跟进去是调用SpringApplication实例中的EventPublishingRunListener的finished方法。
源码:
public void finished(ConfigurableApplicationContext context, Throwable exception) {
// 如果启动中没有抛出异常,则发布ApplicationReadyEvent事件,有抛出异常则发布ApplicationFailedEvent事件。
SpringApplicationEvent event = getFinishedEvent(context, exception);
if (context != null && context.isActive()) {
// Listeners have been registered to the application context so we should
// use it at this point if we can
context.publishEvent(event);
}
else {
// An inactive context may not have a multicaster so we use our multicaster to
// call all of the context's listeners instead
if (context instanceof AbstractApplicationContext) {
for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
.getApplicationListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
if (event instanceof ApplicationFailedEvent) {
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
}
this.initialMulticaster.multicastEvent(event);
}
}
如果启动中没有抛出异常,则发布ApplicationReadyEvent事件,有抛出异常则发布ApplicationFailedEvent事件。
使用ApplicationContext中的SimpleApplicationEventMulticaster发布事件。
下面详细介绍哪些该事件会发布给哪些监听器,以及监听器执行的逻辑
10.1 BackgroundPreinitializer
源码:一个CountDownLatch调用await()等待
public void onApplicationEvent(SpringApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
if (preinitializationStarted.compareAndSet(false, true)) {
performPreinitialization();
}
}
if ((event instanceof ApplicationReadyEvent
|| event instanceof ApplicationFailedEvent)
&& preinitializationStarted.get()) {
try {
preinitializationComplete.await();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
10.2 DelegatingApplicationListener
源码:并没有监听ApplicationReadyEvent事件,并且muiticaster为空,等于什么都没做。
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
List<ApplicationListener<ApplicationEvent>> delegates = getListeners(
((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
if (delegates.isEmpty()) {
return;
}
this.multicaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<ApplicationEvent> listener : delegates) {
this.multicaster.addApplicationListener(listener);
}
}
if (this.multicaster != null) {
this.multicaster.multicastEvent(event);
}
}
10.3 SpringApplicationAdminMXBeanRegistrar
源码:修改flag
public void onApplicationEvent(ApplicationReadyEvent event) {
if (this.applicationContext.equals(event.getApplicationContext())) {
this.ready = true;
}
}
11.计时结束
stopWatch.stop();
源码
public void stop() throws IllegalStateException {
if (!this.running) {
throw new IllegalStateException("Can't stop StopWatch: it's not running");
}
long lastTime = System.currentTimeMillis() - this.startTimeMillis;
this.totalTimeMillis += lastTime;
this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
if (this.keepTaskList) {
this.taskList.add(lastTaskInfo);
}
++this.taskCount;
this.running = false;
this.currentTaskName = null;
}