SpringBoot 原理 (二): run

2016-01-05  本文已影响8450人  龙白一梦

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的同学理解.

上一篇下一篇

猜你喜欢

热点阅读