在上一篇文章中我们详细分析了spring boot是如何准备上下文环境的,今天我们来看一下run函数剩余的内容。还是先把run函数贴出来:

	/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
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);
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;
}

我们接着往下看,来看一下this.refreshContext(context);函数,这个函数用于刷新上下文,跟踪到源码看一下:

	private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}

在refreshContext函数中,第一行调用了refresh(context);跳转了一下,下面的代码是注册了一个应用关闭的函数钩子。

先来看refresh(context);函数:

	/**
* Refresh the underlying {@link ApplicationContext}.
* @param applicationContext the application context to refresh
*/
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}

通过代码跟踪分析发现,其实是调用了AbstractApplicationContext中的refresh方法。

在ServletWebServerApplicationContext和ReactiveWebServerApplicationContext的refresh函数中都是调用了super.refresh();

    public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory); try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
if(this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization \
- cancelling refresh attempt: " + var9);
} this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
} }
}

在这段代码中我们可以看到,其是使用了上下文中的startupShutdownMonitor属性创建一个同步代码库来执行的刷新动作。首先是实验this.prepareRefresh();准备刷新上下文:

    protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if(this.logger.isInfoEnabled()) {
this.logger.info("Refreshing " + this);
} this.initPropertySources();
this.getEnvironment().validateRequiredProperties();
this.earlyApplicationEvents = new LinkedHashSet();
}

接着获取了bean工厂以后,设置了一些bean工厂的环境:

    protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.setBeanClassLoader(this.getClassLoader());
beanFactory.setBeanExpressionResolver(
new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(
new ResourceEditorRegistrar(this, this.getEnvironment()));
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
if(beanFactory.containsBean("loadTimeWeaver")) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(
new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
} if(!beanFactory.containsLocalBean("environment")) {
beanFactory.registerSingleton("environment", this.getEnvironment());
} if(!beanFactory.containsLocalBean("systemProperties")) {
beanFactory.registerSingleton("systemProperties",
this.getEnvironment().getSystemProperties());
} if(!beanFactory.containsLocalBean("systemEnvironment")) {
beanFactory.registerSingleton("systemEnvironment",
this.getEnvironment().getSystemEnvironment());
} }

接下来是发送了一个bean工厂的处理信号,其中ServletWebServerApplicationContext中的实现是在bean工厂里添加了一个WebApplicationContextServletContextAwareProcessor处理器:

    protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(
new WebApplicationContextServletContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
}

紧接着就是调用了这处理器,并且注册到bean工厂。然后就是分别调用了initMessageSource()和initApplicationEventMulticaster()用于初始化监听器和监听管理器:

    protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
if(beanFactory.containsLocalBean("messageSource")) {
this.messageSource = (MessageSource)beanFactory
.getBean("messageSource", MessageSource.class);
if(this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
HierarchicalMessageSource dms = (HierarchicalMessageSource)this.messageSource;
if(dms.getParentMessageSource() == null) {
dms.setParentMessageSource(this.getInternalParentMessageSource());
}
} if(this.logger.isDebugEnabled()) {
this.logger.debug("Using MessageSource [" + this.messageSource + "]");
}
} else {
DelegatingMessageSource dms1 = new DelegatingMessageSource();
dms1.setParentMessageSource(this.getInternalParentMessageSource());
this.messageSource = dms1;
beanFactory.registerSingleton("messageSource", this.messageSource);
if(this.logger.isDebugEnabled()) {
this.logger.debug("Unable to locate MessageSource with name
\'messageSource\': using default [" + this.messageSource + "]");
}
} } protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
if(beanFactory.containsLocalBean("applicationEventMulticaster")) {
this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory
.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
if(this.logger.isDebugEnabled()) {
this.logger.debug("Using ApplicationEventMulticaster [" +
this.applicationEventMulticaster + "]");
}
} else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton("applicationEventMulticaster",
this.applicationEventMulticaster);
if(this.logger.isDebugEnabled()) {
this.logger.debug("Unable to locate ApplicationEventMulticaster with name
\'applicationEventMulticaster\':
using default [" + this.applicationEventMulticaster + "]");
}
} }

在initMessageSource函数中会首先判断beanFactory工厂中是否已经存在messageSource,如果不存在就会为当前的messageSource创建一个新的DelegatingMessageSource。

initApplicationEventMulticaster的初始化方式跟initMessageSource差不多,这里不再多述。

继续看run函数,接下来调用onRefresh()函数,这个函数只是发送一个刷新的事件,源码中并没有具体的实现。

接着放下看,this.registerListeners();把spring容器内的listener和beanfactory的listener都添加到广播器中:

    protected void registerListeners() {
Iterator listenerBeanNames = this.getApplicationListeners().iterator(); while(listenerBeanNames.hasNext()) {
ApplicationListener earlyEventsToProcess = (ApplicationListener)listenerBeanNames.next();
this.getApplicationEventMulticaster().addApplicationListener(earlyEventsToProcess);
} String[] var6 = this.getBeanNamesForType(ApplicationListener.class, true, false);
String[] var7 = var6;
int var3 = var6.length; for(int earlyEvent = 0; earlyEvent < var3; ++earlyEvent) {
String listenerBeanName = var7[earlyEvent];
this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
} Set var8 = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if(var8 != null) {
Iterator var9 = var8.iterator(); while(var9.hasNext()) {
ApplicationEvent var10 = (ApplicationEvent)var9.next();
this.getApplicationEventMulticaster().multicastEvent(var10);
}
} }

通过this.finishBeanFactoryInitialization(beanFactory);实例化BeanFactory 中已经被注册但是没被实例化的所有实例。初始化的过程中各种BeanPostProcessor就开始生效了:

    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
if(beanFactory.containsBean("conversionService") &&
beanFactory.isTypeMatch("conversionService", ConversionService.class)) { beanFactory.setConversionService((ConversionService)beanFactory
.getBean("conversionService", ConversionService.class));
} if(!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver((strVal) -> {
return this.getEnvironment().resolvePlaceholders(strVal);
});
} String[] weaverAwareNames = beanFactory
.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
String[] var3 = weaverAwareNames;
int var4 = weaverAwareNames.length; for(int var5 = 0; var5 < var4; ++var5) {
String weaverAwareName = var3[var5];
this.getBean(weaverAwareName);
} beanFactory.setTempClassLoader((ClassLoader)null);
beanFactory.freezeConfiguration();
beanFactory.preInstantiateSingletons();
}

接下来调用finishRefresh()函数用于完成刷新后的一些扫尾工作,包括产生的缓存、初始化生命周期处理器LifecycleProcessor,并调用其onRefresh()方法、发布事件、调用LiveBeansView的registerApplicationContext注册context。

    protected void finishRefresh() {
this.clearResourceCaches();
this.initLifecycleProcessor();
this.getLifecycleProcessor().onRefresh();
this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this)));
LiveBeansView.registerApplicationContext(this);
}

接着看run函数,在刷新完context后,调用了一个afterRefresh函数,这个函数前面已经说过了,是为了给ApplicationContext的子类留下的一个扩展点。

然后调用了listeners.started(context);,把监听器设置成了已经启动的状态。

最后调用了callRunners函数,获取所有的ApplicationRunner和CommandLineRunner然后调用他们的run方法:

	private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}

总结:spring boot 2.0在启动的时候,首先会调用SpringApplication的构造函数进行初始化,调用实例函数run,在run函数中,首先获取监听器,并设置成启动状态,后面准备环境prepareEnvironment,准备prepareContext上下文,刷新上下文refreshContext,最后调用callRunners来依次调用注册的Runner。

spring boot 2.0 源码分析(五)的更多相关文章

  1. spring boot 2.0 源码分析(一)

    在学习spring boot 2.0源码之前,我们先利用spring initializr快速地创建一个基本的简单的示例: 1.先从创建示例中的main函数开始读起: package com.exam ...

  2. spring boot 2.0 源码分析(四)

    在上一章的源码分析里,我们知道了spring boot 2.0中的环境是如何区分普通环境和web环境的,以及如何准备运行时环境和应用上下文的,今天我们继续分析一下run函数接下来又做了那些事情.先把r ...

  3. spring boot 2.0 源码分析(二)

    在上一章学习了spring boot 2.0启动的大概流程以后,今天我们来深挖一下SpringApplication实例变量的run函数. 先把这段run函数的代码贴出来: /** * Run the ...

  4. spring boot 2.0 源码分析(三)

    通过上一章的源码分析,我们知道了spring boot里面的listeners到底是什么(META-INF/spring.factories定义的资源的实例),以及它是创建和启动的,今天我们继续深入分 ...

  5. Spring Boot 自动配置 源码分析

    Spring Boot 最大的特点(亮点)就是自动配置 AutoConfiguration 下面,先说一下 @EnableAutoConfiguration ,然后再看源代码,到底自动配置是怎么配置的 ...

  6. Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五)

    Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五) 题记:关于SolrCloud的Recovery策略已经写了四篇了,这篇应该是系统介绍Recovery策略的最后一篇了 ...

  7. Spring Cloud 学习 之 Spring Cloud Eureka(源码分析)

    Spring Cloud 学习 之 Spring Cloud Eureka(源码分析) Spring Boot版本:2.1.4.RELEASE Spring Cloud版本:Greenwich.SR1 ...

  8. Solr4.8.0源码分析(11)之Lucene的索引文件(4)

    Solr4.8.0源码分析(11)之Lucene的索引文件(4) 1. .dvd和.dvm文件 .dvm是存放了DocValue域的元数据,比如DocValue偏移量. .dvd则存放了DocValu ...

  9. Solr4.8.0源码分析(10)之Lucene的索引文件(3)

    Solr4.8.0源码分析(10)之Lucene的索引文件(3) 1. .si文件 .si文件存储了段的元数据,主要涉及SegmentInfoFormat.java和Segmentinfo.java这 ...

随机推荐

  1. JavaBeansDataExchange could not instantiate result class

    当ibatis初始化Bean的时候,会调用无参的构造函数,所以如果Bean中有带参的构造函数,一定得多写个无参的构造函数, 否则ibatis会因找不到构造函数而出错,抛出异常如下:JavaBeansD ...

  2. 利用Maven插件将依赖包、jar/war包及配置文件输出到指定目录

    写在前面 ​ 最近遇到一个朋友遇到一个项目需要将maven的依赖包和配置文件分开打包然后用脚本执行程序.这样的好处在于可以随时修改配置文件内容及查看jar包.如果将所有打成一个jar包就会有个问题(例 ...

  3. time模块案例演示

    案例01: 2008年8月8日20:08:08 往后88,888,888秒是哪天?星期几? 日期->时间戳(浮点数)->可以做数学运算 演示: import time # 构造日期的元组, ...

  4. vue2.0学习笔记之路由(二)路由嵌套+动画

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. HTML常用标签大全

  6. Universal-Image-Loader源码分析(一)——ImageLoaderConfiguration分析

    UIl与Volley一样是非常古老的框架,UIL实现了从网络获取图片,对图片进行缓存,以及根据个性化的设置来将图片加载到ImageView上. 这篇文章 主要分析UIl在初始化配置的源码 UIL初始化 ...

  7. BZOJ1007:[HNOI2008]水平可见直线(计算几何)

    Description 在xoy直角坐标平面上有n条直线L1,L2,...Ln,若在y值为正无穷大处往下看,能见到Li的某个子线段,则称Li为 可见的,否则Li为被覆盖的. 例如,对于直线: L1:y ...

  8. linux ssh 应用

    linux 服务器 连接另一个linux服务器 ssh 用户名@IP地址 linux 服务器传输文件到另一个linux服务器 scp 文件名(可多个)  用户名@IP地址:传到的目录 /home

  9. 20165302 程上杰 Exp2 后门原理与实践

    一,后门概念 后门就是不经过正常认证流程而访问系统的通道. 二,后门工具 1.netcat(nc.ncat) 是一个底层工具,进行基本的TCP UDP数据收发.常被与其他工具结合使用,起到后门的作用. ...

  10. 原生js switch语句

    一.我们在流判断的时候,我们大多数的情况我使用if  else 语句.但是对于一些大量的逻辑的判断的时候,我们不建议使用if elseif语句 这种语句的效率执行不高,因为他每个expression ...