我们来分析SpringApplication启动流程中的run()方法,代码如下

    public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
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);
}
}

看起来方法比较长,不过我们主要看启动流程,哪些监控,失败分析的我们以后再说,所以这个方法中关键的几步如下

 context = createApplicationContext();
prepareContext(context,environment,listeners,applicationArguments,printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);

没错,看到关键的方法就是这4个,我们首先分析 createApplicationContext()方法,代码如下

  public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext"; protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
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环境下它加载并创建了 ConfigurableApplicationContext,简单点说就是创建了ApplicationContext,即创建了Bean的容器(BeanFactory)

然后看 prepareContext()方法,可以看出,这个方法是将配置,参数,监听器,以及banner信息注入applicationContext中

    private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
} // Add boot specific singleton beans
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()]));
listeners.contextLoaded(context);
}

方法3是启动的关键函数,在这一步中springboot已经启动,因为比较复杂,我们稍后说。所以我么先看 afterRefresh(context, applicationArguments),其细节如下

    protected void afterRefresh(ConfigurableApplicationContext context,
ApplicationArguments args) {
callRunners(context, args);
} 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);
}
}
}   private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
try {
(runner).run(args.getSourceArgs());
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
}
}

看到这里大家想起来什么了没有?没错,我们自己现实的 ApplicationRunner 或 CommandLineRunner 这些类就是在这里被调用执行的。

最后我们来看最重要的 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();
} public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh(); // Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory); // Initialize message source for this context.
initMessageSource(); // Initialize event multicaster for this context.
initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.
onRefresh(); // Check for listener beans and register them.
registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event.
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方法中。

这个方法的每一步上都有明确的注释。

prepareRefresh()准备刷新,设置应用的开启时间,设置active标志,并执行一些属性的初始化工作。

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();获取BeanFactory。

prepareBeanFactory(beanFactory)执行一些BeanFactory的初始化工作。

postProcessBeanFactory(beanFactory)设置BeanFactory的后置处理器。(可以参考Spring中Bean的生命周期)

invokeBeanFactoryPostProcessors(beanFactory)调用BeanFactory的后置处理器。

registerBeanPostProcessors(beanFactory)在容器内的所有Bean实例化之前注册Bean的前(后)置处理器。(可以参考BeanPostProcessor)

initMessageSource()设置messgeSource

initApplicationEventMulticaster()初始化Context的广播器

onRefresh()根据子类的实现方式不同做不同的事。EmbeddedWebApplicationContext会创建内置的Tomcat并启动。

registerListeners()创建监听器

finishBeanFactoryInitialization(beanFactory)实例化BeanFactory中非懒加载的单例的Bean。这个方法比较关键。

finishRefresh()初始化或启动声明周期相关的任务,发布Context已经刷新的消息。

以上是springboot启动的粗略流程,让我们大致明白启动的流程是什么样的。各个步骤解释的不是特别详细,也有可能还有错误。

以后可能会逐步的进行详细的分析。

引出的问题

BeanFactory是什么。

Tomcat中和BeanFactory中生命周期的问题。

Spring中的事件监听机制。

Tomcat和Servlet和Springboot的关联。

SpringBoot的启动流程分析(2)的更多相关文章

  1. SpringBoot的启动流程分析(1)

    通过分析我们可以找到 org.springframework.boot.SpringApplication 中如下, public static ConfigurableApplicationCont ...

  2. SpringBoot启动流程分析(二):SpringApplication的run方法

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  3. SpringBoot启动流程分析(五):SpringBoot自动装配原理实现

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  4. SpringBoot启动流程分析(六):IoC容器依赖注入

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  5. SpringBoot启动流程分析(一):SpringApplication类初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  6. SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  7. SpringBoot启动流程分析(四):IoC容器的初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

  8. SpringBoot IoC启动流程、初始化过程及Bean生命周期各个阶段的作用

    目录 SpringBoot IoC启动流程.初始化过程及Bean生命周期各个阶段的作用 简述 首先明确IoC容器是啥 准备-SpringApplication的实例化 启动-SpringApplica ...

  9. SpringBoot的启动流程是怎样的?SpringBoot源码(七)

    注:该源码分析对应SpringBoot版本为2.1.0.RELEASE 1 温故而知新 本篇接 SpringBoot内置的各种Starter是怎样构建的? SpringBoot源码(六) 温故而知新, ...

随机推荐

  1. XML字符串转为Map集合

    public class xmlToMapUtils { /** * xml字符串转为map集合 * @param xmlStr * @return */ public static Map<S ...

  2. 《MySQL数据库》常用语法(一)

    MySQL从创建数据库到对表的增删改操作汇总. 1. 数据库操作: -- 查看所有的数据库 SHOW DATABASES ; -- 创建一个数据库,XXX表示数据库名称 CREATE DATABASE ...

  3. B站上传字幕问题解决

    博客:blog.shinelee.me | 博客园 | CSDN B站上传字幕时,如果srt文件中出现如下空行,则会报错,仅上传了空行前的部分 于是写了个python脚本,如下: import pys ...

  4. mySql的case when用法

    背景 有点忘了,记录下 写法一 case colume when condition then result when condition then result when condition the ...

  5. EFCore的外键级联删除导致的【可能会导致循环或多重级联路径】

    之前也是经常遇到这个问题,但好在每次创建的实体不多,很容易就能找到是哪个外键导致级联循环删除问题 之前都是这么处理,因为创建的实体也不多,所以还处理得来 但最近跟别人合作写后端,别人写了好多实体,我一 ...

  6. git 本地代码 切换远程分支

    公司之前代码使用的是gitlab,后来换成腾讯的工峰,所以需要切换远程不支,所以在原代码上切换即可. 在原项目打开git bash命令,打开后会显示本地的原始分支 打开后 添加新的远程分支,红色字体为 ...

  7. Gradle for Android ( 构建变体 )

    链接: http://77blogs.com/?p=38 https://www.cnblogs.com/tangZH/p/10999060.html 有时候我们一个app需要有不同的版本,不同的版本 ...

  8. iOS 13 presentViewController

    升级了iOS 13,发现代码中使用presentViewController的都变成了这样的,顶部留了一部分 查看present样式,iOS 13 默认自动适配,需要在present的时候,设置sty ...

  9. Linux中环境变量相关文件的区别

    Linux下各种不同环境变量相关文件的作用: 1. /etc/environment  设置整个系统的环境,系统启动时,该文件被执行. 2. /etc/profile     设置所有用户的环境,当用 ...

  10. <离散数学>代数系统——群,半群

    ------运算的定义及性质 设S是一个非空集合,映射f:Sn->S称为S上的一个n元运算.假设“•”是定义在集合S上的一个二元运算.若: ∀x,y∈S,x•y∈S,则称“•”在S上是封闭的. ...