先看示例html
SpringBoot的啓動很簡單,在許多狀況下,你能夠委託給靜態SpringApplication.run
方法,代碼以下:java
@SpringBootApplication public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } }
從代碼上能夠看出,調用了SpringApplication的靜態方法run。這個run方法會構造一個SpringApplication的實例,而後再調用這裏實例的run方法就表示啓動SpringBoot。git
當你的應用程序啓動時,你應該看到相似於如下輸出的內容:github
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.10.RELEASE)
所以,想要分析SpringBoot的啓動過程,咱們須要熟悉SpringApplication的構造過程以及SpringApplication的run方法執行過程便可。web
咱們以上述這段代碼爲例,分析SpringBoot的啓動過程。spring
SpringApplication構造的時候內部會調用一個private方法initialize:springboot
public SpringApplication(Object... sources) { initialize(sources); // sources目前是一個MyApplication的class對象 } private void initialize(Object[] sources) { if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); // 把sources設置到SpringApplication的sources屬性中,目前只是一個MyApplication類對象 } this.webEnvironment = deduceWebEnvironment(); // 判斷是不是web程序,若是(javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext)都在當前的類加載器中,則爲true不然爲false,判斷結果設置到webEnvironment屬性中
// 從spring.factories文件中找出key爲ApplicationContextInitializer的類並實例化後設置到SpringApplication的initializers屬性中。這個過程也就是找出全部的應用程序初始化器 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
// 從spring.factories文件中找出key爲ApplicationListener的類並實例化後設置到SpringApplication的listeners屬性中。這個過程就是找出全部的應用程序事件監聽器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 找出main類,這裏是MyApplication類 this.mainApplicationClass = deduceMainApplicationClass(); }
下面分別介紹下載SpringApplication的構造中,涉及到的幾個類:app
1.一、ApplicationContextInitializer,應用程序初始化器,作一些初始化的工做:less
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> { void initialize(C applicationContext); }
Springboot中目前有以下這些ContextInitializer,看截圖:ide
1.二、ApplicationListener,應用程序事件(ApplicationEvent)監聽器:
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { void onApplicationEvent(E event); }
這裏的應用程序事件(ApplicationEvent)有:應用程序啓動事件(ApplicationStartedEvent)、失敗事件(ApplicationFailedEvent)、準備事件(ApplicationPreparedEvent)等。
應用程序事件監聽器跟監聽事件是綁定的。好比:
默認狀況下,initialize方法從spring.factories文件中找出的key爲ApplicationContextInitializer的類有:
org.springframework.boot.context.config.DelegatingApplicationContextInitializer
org.springframework.boot.context.ContextIdApplicationContextInitializer
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer
org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
key爲ApplicationListener的有:
org.springframework.boot.context.config.ConfigFileApplicationListener
org.springframework.boot.context.config.AnsiOutputApplicationListener
org.springframework.boot.logging.LoggingApplicationListener
org.springframework.boot.logging.ClasspathLoggingApplicationListener
org.springframework.boot.autoconfigure.BackgroundPreinitializer
org.springframework.boot.context.config.DelegatingApplicationListener
org.springframework.boot.builder.ParentContextCloserApplicationListener
org.springframework.boot.context.FileEncodingApplicationListener
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
SpringApplication的run方法代碼以下:
public ConfigurableApplicationContext run(String... args) { //①開啓任務執行時間監聽器 StopWatch stopWatch = new StopWatch(); // 開始執行,記錄開始時間 stopWatch.start(); ConfigurableApplicationContext context = null; FailureAnalyzers analyzers = null; //設置系統屬性『java.awt.headless』,爲true則啓用headless模式支持 configureHeadlessProperty(); //②經過*SpringFactoriesLoader*檢索*META-INF/spring.factories*, //找到聲明的全部SpringApplicationRunListener的實現類並將其實例化, //以後逐個調用其started()方法,廣播SpringBoot要開始執行了。 SpringApplicationRunListeners listeners = getRunListeners(args); //至關於批量的發佈事件,及批量的監聽器處理事件進行相應的操做 listeners.starting(); try { //構造一個應用程序參數持有類 ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); //③建立並配置當前SpringBoot應用將要使用的Environment(包括配置要使用的PropertySource以及Profile), //並遍歷調用全部的SpringApplicationRunListener的environmentPrepared()方法,廣播Environment準備完畢。 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //④決定是否打印Banner Banner printedBanner = printBanner(environment); //⑤根據webEnvironment的值來決定建立何種類型的ApplicationContext對象 //若是是web環境,則建立org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext //不然建立org.springframework.context.annotation.AnnotationConfigApplicationContext context = createApplicationContext(); //⑥-1註冊異常分析器 analyzers = new FailureAnalyzers(context); //⑥-2爲ApplicationContext加載environment,以後逐個執行ApplicationContextInitializer的initialize()方法來進一步封裝ApplicationContext, //並調用全部的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一個空的contextPrepared()方法】, //以後初始化IoC容器,並調用SpringApplicationRunListener的contextLoaded()方法,廣播ApplicationContext的IoC加載完成, //這裏就包括經過**@EnableAutoConfiguration**導入的各類自動配置類。 prepareContext(context, environment, listeners, applicationArguments, printedBanner); //⑦初始化全部自動配置類,調用ApplicationContext的refresh()方法 refreshContext(context); //⑧遍歷全部註冊的ApplicationRunner和CommandLineRunner,並執行其run()方法。 //該過程能夠理解爲是SpringBoot完成ApplicationContext初始化前的最後一步工做, //咱們能夠實現本身的ApplicationRunner或者CommandLineRunner,來對SpringBoot的啓動過程進行擴展。 afterRefresh(context, applicationArguments); //⑨調用全部的SpringApplicationRunListener的finished()方法,廣播SpringBoot已經完成了ApplicationContext初始化的所有過程。 listeners.finished(context, null); //⑩關閉任務執行時間監聽器 stopWatch.stop(); //⑪若是開啓日誌,則打印執行是時間
if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } return context; } catch (Throwable ex) { //⑫調用異常分析器打印報告,調用全部的SpringApplicationRunListener的finished()方法將異常信息發佈出去
handleRunFailure(context, listeners, analyzers, ex); throw new IllegalStateException(ex); } }
相關的分析:
①StopWatch,StopWatch相關見《Spring計時器StopWatch使用》
分析run方法以前,先看一下SpringApplication中的一些事件和監聽器概念。
②、首先是SpringApplicationRunListeners類和SpringApplicationRunListener類的介紹
SpringApplicationRunListeners內部持有SpringApplicationRunListener集合和1個Log日誌類。用於SpringApplicationRunListener監聽器的批量執行。
class SpringApplicationRunListeners { private final Log log; private final List<SpringApplicationRunListener> listeners;
SpringApplicationRunListener看名字也知道用於監聽SpringApplication的run方法的執行。
它定義了5個步驟:
SpringApplicationRunListener目前只有一個實現類EventPublishingRunListener,它把監聽的過程封裝成了SpringApplicationEvent事件並讓內部屬性(屬性名爲multicaster)ApplicationEventMulticaster接口的實現類SimpleApplicationEventMulticaster廣播出去,廣播出去的事件對象會被SpringApplication中的listeners屬性進行處理。
因此說SpringApplicationRunListener和ApplicationListener之間的關係是經過ApplicationEventMulticaster廣播出去的SpringApplicationEvent所聯繫起來的。更多的spring事件知識見《ApplicationEvent事件機制源碼分析》
③、SpringBoot的Environment的準備
private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 建立SpringBoot的Environment,多是StandardServletEnvironment或者StandardEnvironment ConfigurableEnvironment environment = getOrCreateEnvironment(); //包括配置要使用的PropertySource以及Profile configureEnvironment(environment, applicationArguments.getSourceArgs()); //遍歷調用全部的Environment的environmentPrepared方法,目前只有一個實現類EventPublishRunListener,即調用其multicastEvent,廣播事件 listeners.environmentPrepared(environment); if (!this.webEnvironment) { //將給定環境轉換爲標準環境。若是環境已是一個標準環境,而不是ConfigurableWebEnvironment,則不執行轉換,而且不改變返回值。 environment = new EnvironmentConverter(getClassLoader()) .convertToStandardEnvironmentIfNecessary(environment); } return environment; }
④決定是否打印Banner
private Banner printBanner(ConfigurableEnvironment environment) { //判斷是否開啓打印,若是沒有開啓,直接返回不執行下面的打印邏輯 if (this.bannerMode == Banner.Mode.OFF) { return null; } //讀取banner文件 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); }
⑤建立ApplicationContext
protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { //經過反射得到ApplicationContext的類信息
// 若是是web程序,那麼構造org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext容器
// 不然構造org.springframework.context.annotation.AnnotationConfigApplicationContext容器
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); } } //經過類的newInstance建立實例 return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass); } public static <T> T instantiate(Class<T> clazz) throws BeanInstantiationException { Assert.notNull(clazz, "Class must not be null"); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { return clazz.newInstance(); } catch (InstantiationException ex) { throw new BeanInstantiationException(clazz, "Is it an abstract class?", ex); } catch (IllegalAccessException ex) { throw new BeanInstantiationException(clazz, "Is the constructor accessible?", ex); } }
⑥-一、註冊異常分析器
public FailureAnalyzers(ConfigurableApplicationContext context) { this(context, null); } FailureAnalyzers(ConfigurableApplicationContext context, ClassLoader classLoader) { Assert.notNull(context, "Context must not be null"); this.classLoader = (classLoader == null ? context.getClassLoader() : classLoader); //經過反射,批量建立FailureAnalyzer,順序的放到list集合裏 this.analyzers = loadFailureAnalyzers(this.classLoader); prepareFailureAnalyzers(this.analyzers, context); } //遍歷爲每一個FailureAnalyzer調用prepareAnalyzer private void prepareFailureAnalyzers(List<FailureAnalyzer> analyzers, ConfigurableApplicationContext context) { for (FailureAnalyzer analyzer : analyzers) { prepareAnalyzer(context, analyzer); } } //將Analyzer擁有的工廠賦值給上面新建立的Analyzer,提供給Analyzer實例的回調。 //在填充普通bean屬性以後調用,但在初始化回調(如InitializingBean.afterPropertiesSet()或自定義init-method)以前調用。 private void prepareAnalyzer(ConfigurableApplicationContext context, FailureAnalyzer analyzer) { if (analyzer instanceof BeanFactoryAware) { ((BeanFactoryAware) analyzer).setBeanFactory(context.getBeanFactory()); } }
⑥-二、ApplicationContext的初始化(environment、initializer)
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { //爲context設置上面的Environment context.setEnvironment(environment); //在ApplicationContext中應用任何相關的後處理。子類能夠根據須要應用額外的處理。 postProcessApplicationContext(context); //在刷新上下文以前,將全部的applicationcontextinitialalizer應用於上下文。這裏是遍歷執行全部的initialize()來進一步封裝ApplicationContext 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()])); //調用SpringApplicationRunListener的contextLoaded()方法,廣播ApplicationContext的IoC加載完成 listeners.contextLoaded(context); } //初始化器作的工做,好比ContextIdApplicationContextInitializer會設置應用程序的id;AutoConfigurationReportLoggingInitializer會給應用程序添加一個條件註解解析器報告等:
//在刷新上下文以前,將全部的applicationcontextinitialalizer應用於上下文。 protected void applyInitializers(ConfigurableApplicationContext context) { for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } }
⑦refreshContext
Spring容器的刷新refresh方法內部會作不少不少的事情:好比BeanFactory的設置,BeanFactoryPostProcessor接口的執行、BeanPostProcessor接口的執行、自動化配置類的解析、條件註解的解析、國際化的初始化等等。這部份內容會在以後的文章中進行講解。
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(); }
⑧遍歷全部註冊的ApplicationRunner和CommandLineRunner,並執行其run()方法
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { callRunners(context, args); } private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<Object>(); // 找出Spring容器中ApplicationRunner接口的實現類 runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); // 找出Spring容器中CommandLineRunner接口的實現類 runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); // 對runners進行排序 AnnotationAwareOrderComparator.sort(runners); // 遍歷runners依次執行 for (Object runner : new LinkedHashSet<Object>(runners)) { // 若是是ApplicationRunner,進行ApplicationRunner的run方法調用 if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } // 若是是CommandLineRunner,進行CommandLineRunner的run方法調用 if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } }
⑨調用全部的SpringApplicationRunListener的finished()方法,廣播SpringBoot已經完成了ApplicationContext初始化的所有過程
public void finished(ConfigurableApplicationContext context, Throwable exception) { for (SpringApplicationRunListener listener : this.listeners) { callFinishedListener(listener, context, exception); } } private void callFinishedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context, Throwable exception) { try { listener.finished(context, exception); } catch (Throwable ex) { if (exception == null) { ReflectionUtils.rethrowRuntimeException(ex); } if (this.log.isDebugEnabled()) { this.log.error("Error handling failed", ex); } else { String message = ex.getMessage(); message = (message == null ? "no error message" : message); this.log.warn("Error handling failed (" + message + ")"); } } } @Override public void finished(ConfigurableApplicationContext context, Throwable exception) { //建立springboot初始化完成事件 SpringApplicationEvent event = getFinishedEvent(context, exception); if (context != null && context.isActive()) { //活動上下文 context.publishEvent(event); } else { //非活動上下文沒有多播程序,直接使用initialMulticaster進行廣播 if (context instanceof AbstractApplicationContext) { for (ApplicationListener<?> listener : ((AbstractApplicationContext) context) .getApplicationListeners()) { this.initialMulticaster.addApplicationListener(listener); } } if (event instanceof ApplicationFailedEvent) { this.initialMulticaster.setErrorHandler(new LoggingErrorHandler()); } this.initialMulticaster.multicastEvent(event); } } private SpringApplicationEvent getFinishedEvent( ConfigurableApplicationContext context, Throwable exception) { //若是出錯了,返回ApplicationFailedEvent的事件 if (exception != null) { return new ApplicationFailedEvent(this.application, this.args, context, exception); } return new ApplicationReadyEvent(this.application, this.args, context); }
⑫調用異常分析器打印報告,調用全部的SpringApplicationRunListener的finished()方法將異常信息發佈出去
private void handleRunFailure(ConfigurableApplicationContext context, SpringApplicationRunListeners listeners, FailureAnalyzers analyzers, Throwable exception) { try { try { handleExitCode(context, exception); listeners.finished(context, exception); } finally { reportFailure(analyzers, exception); if (context != null) { context.close(); } } } catch (Exception ex) { logger.warn("Unable to close ApplicationContext", ex); } ReflectionUtils.rethrowRuntimeException(exception); } private void handleExitCode(ConfigurableApplicationContext context, Throwable exception) { //根據Exception獲得退出碼 int exitCode = getExitCodeFromException(context, exception); //若是exitCode不爲0,發佈ExitCodeEvent事件 if (exitCode != 0) { if (context != null) { context.publishEvent(new ExitCodeEvent(context, exitCode)); } SpringBootExceptionHandler handler = getSpringBootExceptionHandler(); if (handler != null) { handler.registerExitCode(exitCode); } } }
這樣run方法執行完成以後。Spring容器也已經初始化完成,各類監聽器和初始化器也作了相應的工做。
SpringBoot的啓動過程,實際上就是對ApplicationContext的初始化過程。
ApplicationContext建立後馬上爲其設置Environment,並由ApplicationContextInitializer對其進一步封裝。
經過SpringApplicationRunListener在ApplicationContext初始化過程當中各個時點發布各類廣播事件,並由ApplicationListener負責接收廣播事件。
初始化過程當中完成IoC的注入,包括經過@EnableAutoConfiguration導入的各類自動配置類。
初始化完成前調用ApplicationRunner和CommandLineRunner的實現類。
SpringBoot啓動的時候,不論調用什麼方法,都會構造一個SpringApplication的實例,而後調用這個實例的run方法,這樣就表示啓動SpringBoot。
在run方法調用以前,也就是構造SpringApplication的時候會進行初始化的工做,初始化的時候會作如下幾件事:
SpringApplication構造完成以後調用run方法,啓動SpringApplication,run方法執行的時候會作如下幾件事:
寫了一個例子用來驗證分析的啓動邏輯,包括自定義的初始化器、監聽器、ApplicationRunner和CommandLineRunner。
地址在:https://github.com/fangjian0423/springboot-analysis/tree/master/springboot-startup