接上一篇

在创建 SpringApplication 之后, 调用了 run() 方法.

public ConfigurableApplicationContext run(String... args) {
  //定时器, 监控启动时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//java.awt.headless是一种模式用于在缺少显示屏、键盘或者鼠标时的系统配置,很多监控工具如jconsole 需要将该值设置为true,系统变量默认为true
configureHeadlessProperty();
//从 spring.factories 配置中获取监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
   //启动监听器
listeners.starting();
try {
     //对 args 进行封装, 此处args 是 {}
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//构造容器环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//设置需要忽略的bean
configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = printBanner(environment);
//创建容器, 此处创建的是 AnnotationConfigServletWebServerApplicationContext
context = createApplicationContext();
//实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
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);
}
     //遍历调用监听器的started()方法, 发布 ApplicationStartedEvent 事件
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
} try {
     //遍历调用监听器的 running() 方法, 发布 ApplicationReadyEvent 事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}

configureHeadlessProperty()

private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
private boolean headless = true; private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

这里会默认设置为 true.

getRunListeners()

private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}

这里是获取配置的监听器, 并封装到  SpringApplicationRunListeners 了中的 this.liseners 属性中.

这里获取到的是: org.springframework.boot.context.event.EventPublishingRunListener

listeners 这块内容比较多, 放在后面去详细解析

prepareEnvironment()

private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
//根据webApplicationType, 创建运行环境, 此处创建的是 StandardServletEnviroment
ConfigurableEnvironment environment = getOrCreateEnvironment();
   //配置环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
   //给监听器设置环境,遍历监听器,调用其 enviromentParepared() 方法,发布环境已准备事件
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (this.webApplicationType == WebApplicationType.NONE) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}

configureIgnoreBeanInfo()

public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore";

private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
if (System.getProperty(
CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
Boolean ignore = environment.getProperty("spring.beaninfo.ignore",
Boolean.class, Boolean.TRUE);
System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME,
ignore.toString());
}
}

默认设置 spring.beaninfo.ignore 为 true

printBanner()

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 ,  在 application.yml 中, 加入配置

spring:
main:
banner-mode: "off"
可以关闭 banner 打印, 当然这里也可以自定义自己的 banner . 可有可无的功能, 不去管它. 打印就打印吧. 
 

createApplicationContext()

public static final String DEFAULT_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"; public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext"; protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_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);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

根据环境类型, 创建 ApplicationContext 对象 :  AnnotationConfigServletWebServerApplicationContext

 

prepareContext()

private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
//容器的后置处理
postProcessApplicationContext(context);
//对this.initializers中存放的类进行初始化操作, 调用其initialize()方法
applyInitializers(context);
//遍历监听器, 调用其 contextPrepared() 方法, 进行容器准备好的操作, 此处为空操作, 留给子类重写扩展
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);
} //拿this.primarySources + this.sources, 此处拿到的是SbmvcApplication
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//将SbmvcApplication注册到容器里
load(context, sources.toArray(new Object[0]));
//遍历监听器, 调用其contextLoaded()方法, 进行容器加载后的操作
listeners.contextLoaded(context);
}
 

refreshContext()

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();
} @Override
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();
}
}
}

这里执行的内容非常多, 不在此解析了.

afterRefresh()

protected void afterRefresh(ConfigurableApplicationContext context,
ApplicationArguments args) {
}

这里是一个空方法, 可以留个子类重写, 进行扩展操作

callRunners()

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);
}
}
}

执行到这里是, 容器里面还没有哪两个类, 所以 runners.size() = 0. 没有执行什么逻辑.

总结:

run() 方法主要进行了以下几个操作

1. 获取并启动了监听器, 发布了 容器启动事件(ApplicationStartingEvent )

2. 构造容器环境

3. 创建容器

4. 实例化 SpringBootExceptionReporter , 用来支持报告关于启动的错误

5. 准备容器

6. 刷新容器

7. 执行刷新完容器的操作(暂时为空操作)

8. 发布了 容器启动完成事件(ApplicationStartedEvent)

9. 发布了 容器已准备好事件(ApplicationReadyEvent), 如果执行失败, 则会发布容器失败事件(ApplicationFailedEvent)

springboot web - 启动(2) run()的更多相关文章

  1. springboot web - 启动(1) 创建SpringApplication

    一. 测试代码 @SpringBootApplication public class SbmvcApplication { public static void main(String[] args ...

  2. springboot web - 启动(4) tomcat

    接第二篇 第二篇里面, 看到容器创建的是 AnnotationConfigServletWebServerApplicationContext 类型. 一 .类图 二. 构造 public Gener ...

  3. springboot web开发【转】【补】

    pom.xml引入webjars的官网 https://www.webjars.org/ https://www.thymeleaf.org/doc/tutorials/3.0/usingthymel ...

  4. springboot:非web启动

    需要运行一些调度任务,但是又不想放到web容器中运行. 见红色代码: import java.util.concurrent.ThreadPoolExecutor; import org.spring ...

  5. springboot之启动原理解析

    前言 SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起一步步揭开SpringBoot的神秘面 ...

  6. Springboot 项目启动后执行某些自定义代码

    Springboot 项目启动后执行某些自定义代码 Springboot给我们提供了两种"开机启动"某些方法的方式:ApplicationRunner和CommandLineRun ...

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

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

  8. 深入理解SpringBoot之启动探究

    SpringApplication是SpringBoot的启动程序,我们通过它的run方法可以快速启动一个SpringBoot应用.可是这里面到底发生了什么?它是处于什么样的机制简化我们程序启动的?接 ...

  9. java框架之SpringBoot(10)-启动流程及自定义starter

    启动流程 直接从 SpringBoot 程序入口的 run 方法看起: public static ConfigurableApplicationContext run(Object source, ...

随机推荐

  1. 搭建django项目连接mysql数据库环境

    开通博客园这么久,即将写下第一篇博客,十分兴奋.首先了,庆祝自己写下了码农生涯博客园第一篇博客,其次了,庆祝自己经过了10个小时奋战,终于成功搭建django项目连接mysql数据库的环境.在此过程中 ...

  2. 【题解】删数问题(Noip1994)

    题目 时间限制: 1000 ms 内存限制: 65536 KB 提交数: 11506 通过数: 3852 [题目描述] 输入一个高精度的正整数n,去掉其中任意s个数字后剩下的数字按原左右次序组成一个新 ...

  3. Linux gcc链接动态库出错:LIBRARY_PATH和LD_LIBRARY_PATH的区别

    昨天在自己的CentOs7.1上写makefile的时候,发现在一个C程序在编译并链接一个已生成好的lib动态库的时候出错.链接命令大概是这样的: [root@typecodes tcpmsg]# g ...

  4. postman批量接口测试/批量导入/批量参数化简要全过程及遇到问题处理方法

    简单说明下postman批量接口调用的过程及注意事项: 1.报文调试(建议先调通再批量执行,统筹安排时间) 2.参数化,例如: "address": "{{address ...

  5. NR / 5G - Downlink Carrier Waveform

  6. Coroutine 练习 1 - Coroutine Exercises 1

    Coroutine 练习 1 - Coroutine Exercises 1 字典中为动词 “to yield” 给出了两个释义:产出和让步.对于 Python 生成器中的 yield 来 说,这两个 ...

  7. 常用命令 find chmod

    find   path   -option   [   -print ]   [ -exec   -ok   command ]   {} \; find  [指定查找目录]  [查找规则]  [查找 ...

  8. [Redis-CentOS7]Redis集合操作(四)

    SADD 集合添加 127.0.0.1:6379> SADD bbs discuz.net (integer) 1 127.0.0.1:6379> SADD bbs "tiany ...

  9. 珠峰-node

    ##### 文件流的读写. ##### 文件流对pipe的封装. ####

  10. window.location操作url对象

    URL即:统一资源定位符 (Uniform Resource Locator, URL) 完整的URL由这几个部分构成:scheme://host:port/path?query#fragment s ...