SpringBoot 原理 (二): run

SpringApplication 在各种赋值都完成, 初始化部分已完成,下一步是根据初始化的各种配置,对应用进行启动,包括bean的加载,配置文件的读入等等, 调用的方法为:

springApplication.run(args);

源码(版本为Spring-boot-1.3.0.M5)如下:

public ConfigurableApplicationContext run(String... args) {
 StopWatch stopWatch = new StopWatch();
 stopWatch.start();
 ConfigurableApplicationContext context = null;
 configureHeadlessProperty();
 SpringApplicationRunListeners listeners = getRunListeners(args);
 listeners.started();
 try {
 context = doRun(listeners, args);
 stopWatch.stop();
 if (this.logStartupInfo) {
 new StartupInfoLogger(this.mainApplicationClass).logStarted(
 getApplicationLog(), stopWatch);
 }
 return context;
 }
 catch (Throwable ex) {
 try {
 listeners.finished(context, ex);
 this.log.error("Application startup failed", ex);
 }
 finally {
 if (context != null) {
 context.close();
 }
 }
 ReflectionUtils.rethrowRuntimeException(ex);
 return context;
 }
 }

1. StopWatch 监控

用来记录任务的启动 结束时间,是一个非线程的安全的,如果自己使用要考虑多线程的情况.

2. 配置Headless模式, configureHeadlessProperty

private void configureHeadlessProperty() {
 System.setProperty(
 SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
 System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
 Boolean.toString(this.headless)));
 }

用来设置java.awt.headless 属性是true 还是false, java.awt.headless是J2SE的一种模式用于在缺少显示屏、键盘或者鼠标时的系统配置,很多监控工具如jconsole 需要将该值设置为true.

3. SpringApplicationRunListener 对ApplicationListener事件处理

在初始化的时候 我们加载了一批 ApplicationListener,包括springboot自启动的 ,也可以有用户定义ApplicationListener,SpringApplicationRunListener就负责来加载ApplicationListener中的业务.

/**
  * Called immediately when the run method has first started. Can be used for very
  * early initialization.
  */
 void started();

 /**
  * Called once the environment has been prepared, but before the
  * {@link ApplicationContext} has been created.
  * @param environment the environment
  */
 void environmentPrepared(ConfigurableEnvironment environment);

 /**
  * Called once the {@link ApplicationContext} has been created and prepared, but
  * before sources have been loaded.
  * @param context the application context
  */
 void contextPrepared(ConfigurableApplicationContext context);

 /**
  * Called once the application context has been loaded but before it has been
  * refreshed.
  * @param context the application context
  */
 void contextLoaded(ConfigurableApplicationContext context);

 /**
  * Called immediately before the run method finishes.
  * @param context the application context
  * @param exception any run exception or null if run completed successfully.
  */
 void finished(ConfigurableApplicationContext context, Throwable exception);

ApplicationListener可以实现四中event类型, 分别在不同的时候加载, ApplicationEnvironmentPreparedEvent,ApplicationPreparedEvent,ApplicationFailedEvent , ApplicationStartedEvent.
在run的方法中分别使用在:

SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.started();
listeners.environmentPrepared(environment);
listeners.contextPrepared(context);
listeners.finished(context, null);

4. doRun 方法实现

private ConfigurableApplicationContext doRun(SpringApplicationRunListeners listeners,
 String... args) {
 ConfigurableApplicationContext context;
 // Create and configure the environment
 ConfigurableEnvironment environment = getOrCreateEnvironment();
 configureEnvironment(environment, args);
 listeners.environmentPrepared(environment);
 if (this.showBanner) {
 printBanner(environment);
 }

 // Create, load, refresh and run the ApplicationContext
 context = createApplicationContext();
 if (this.registerShutdownHook) {
 try {
 context.registerShutdownHook();
 }
 catch (AccessControlException ex) {
 // Not allowed in some environments.
 }
 }
 context.setEnvironment(environment);
 postProcessApplicationContext(context);
 applyInitializers(context);
 listeners.contextPrepared(context);
 if (this.logStartupInfo) {
 logStartupInfo(context.getParent() == null);
 }

 // Add boot specific singleton beans
 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
 context.getBeanFactory().registerSingleton("springApplicationArguments",
 applicationArguments);

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

 // Refresh the context
 refresh(context);
 afterRefresh(context, applicationArguments);
 listeners.finished(context, null);
 return context;
 }```
#####a. 创建和配置环境相关 
 i. 创建 environment 
        首先根据初始化的配置创建environment的类型, 后面或者property     和profile都是通过这个类来获取的,然后configureEnvironment 就是用来Property和Profiled的.
     
ii. 启动说监听器的environmentPrepared方法,会调用所有监听器中监听ApplicationEnvironmentPreparedEvent的方法,具体逻辑在上一步中已经描述了.

 iii. printBanner 
          打印启动的banner,工业化的项目中可以对此进行定制,使用Banner类即可.

#####b. 创建 加载 刷新 运行ApplicationContext 
 i.创建context
   ```java
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);
 }   

根据是否是webEnvironment 创建不同的context,是创建的是AnnotationConfigEmbedded,WebApplicationContext, 不是创建的默认的 AnnotationConfig, ApplicationContext.

ii.注册关闭的钩子,主要实现当JVM关闭时候,做一些关闭处理,如销毁bean,生命周期关闭等, 实现方式是

 Runtime.getRuntime().addShutdownHook(this.shutdownHook);

JVM关闭时会自动调用.

iii. initializer 初始化 日志启动等其他操作.

c. 配置spring boot特有的单例bean 如commandline相关
d.加载资源,就是在初始化过程中source包含的类
e.context 重新刷新

这个主要都走spring 框架的AbstractApplicationContext#refresh,是对context中的各种资源进行加载, 由于不是boot特有,这里不仔细描述

f.监听器实现所有finished操作

主要的实现ApplicationReadyEvent 或者 ApplicationFailedEvent.

至此,springboot启动过程分析完成了.

5. 后记

本身主要研究分析了boot本身的代码的情况,并没有对spring的代码 和 每个监听器功能进行深入分析后面可开专门文章对此进行补充 ,方便不熟悉spring的同学理解.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容