接上一篇

在创建 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. AJAX的出现与跨域处理

    XMLHttpRequest JSON AJAX CORS 四个名词来开会 如何发请求 在前端的世界里也逛荡了不少日子了,目前已经get到大约5种发起请求的方式,主流的.非主流的. 何种方式 请求方法 ...

  2. HDU_5729_rmq+二分

    http://acm.hdu.edu.cn/showproblem.php?pid=5726 rmq修改成gcd的,关键是找个数,用二分来找,刚开始理解了好久,因为每个区间内gcd是递减的,所以可以优 ...

  3. Educational Codeforces Round 39 Editorial B(Euclid算法,连续-=与%=的效率)

    You have two variables a and b. Consider the following sequence of actions performed with these vari ...

  4. 使用logstash结合logback收集微服务日志

    因为公司开发环境没有装elk,所以每次查看各个微服务的日志只能使用如下命令 这样子访问日志是并不方便,于是想为每个微服务的日志都用logstash收集到一个文件out中,那以后只要输出这个文件则可查看 ...

  5. 清晰架构(Clean Architecture)的Go微服务

    我用Go和gRPC创建了一个微服务项目,并试图找出最好的程序结构,它可以作为我其他项目的模板.我还将程序设计和编程的最佳实践应用于Go Microservice程序,例如清晰架构(Clean Arch ...

  6. OSPF RFC2740

    2.5. Use of link-local addresses IPv6 link-local addresses are for use on a single link, for purpose ...

  7. filebeat+kafka

    kafka出现接收不到filebeat数据,最后发现版本兼容问题 filebeat换成  filebeat-7.4.2-linux-x86_64 kafka是docker-compose启动的,版本是 ...

  8. Angular目录结构

    一. Angular目录结构 e2e:在e2e/下是端到端测试 node_modules:安装的第三方模块都在这,使用npm install安装的1 Src:我们项目的所有文件 { App:组件,以a ...

  9. thinkphp v5.1.36 LTS 如果设置跨域访问

    修改route/route.php中的路由例如 Route::get('new/:id', 'News/read') ->ext('html') ->header('Access-Cont ...

  10. js文本复制插件&vue

    /* HTML: * <a href="javascript:;" class="copy" data-clipboard-text="copy ...