一個讀者,也是個人好朋友投稿的一篇關於 SpringBoot 啓動原理的文章,才大二就如此優秀,將來可期。前端
我一直想了解一下 SpirngBoot 的是如何啓動的,我想就來寫一篇關於 SpirngBoot 啓動分析吧。第一次寫那麼高深的技術話題理解不到位的話也請多多包涵。java
SpinrgBoot 2.0.2react
衆所周知 SpringBoot 的啓動類是在一個 main 方法中調用 SpringApplication.run()
方法啓動的,如:web
@SpringBootApplication public class DiveInSpringBootApplication { public static void main(String[] args) { SpringApplication.run(DiveInSpringBootApplication.class, args); } }
啓動順序分析以下:算法
初始化階段 -> 運行階段
spring
進入run方法中,SpringApplication.run()
會先爲其建立一個 SpringApplication 對象:緩存
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { //加載應用資源(URL資源、File資源、ClassPath資源) this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // primarySources 爲 run 方法傳入的引導類 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //推斷Web應用類型 this.webApplicationType = deduceWebApplicationType(); //加載應用上下文(初始化Initializers) setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); //加載應用事件監聽器(初始化ApplicationListener) setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //推斷引導類 this.mainApplicationClass = deduceMainApplicationClass(); }
Step1. 經過 deduceWebApplicationType()
來推斷咱們Web類型應用服務器
private WebApplicationType deduceWebApplicationType() { //根據當前應用的ClassPath中是否存在相關實現類來推斷Web類型 if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null) && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) { return WebApplicationType.REACTIVE; } for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; }
看看使用到的 3 個常量值:app
常量值 | 應用類型 |
---|---|
REACTIVE_WEB_ENVIRONMENT_CLASS | org.springframework.web.reactive.DispatcherHandler |
MVC_WEB_ENVIRONMENT_CLASS | org.springframework.web.servlet.DispatcherServlet |
WEB_ENVIRONMENT_CLASSES | {"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" } |
也就是說,有以下三種狀況:less
表示當前應用不是一個web應用,啓動時無需加載啓動內嵌的 web 服務器。
Step2. 如何加載應用上下文初始器(初始化 Initializers )
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
利用Spirng的工廠加載機制,實例化ApplicationContextInitializer實現類,並排序集合。具體實現方法以下:
SpringFactoriesLoader.loadFactoryNames
來掃描 META-INF/spring.factories
下符合 ApplicationContextInitializer 類型的資源名稱。META-INF/spring.factories
下找到的資源信息@order
註解和Ordered
接口進行排序# Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ #Spring容器的常見的錯誤配置警告 org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ #設置Spring應用上下文ID org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
Step3. 加載應用事件監聽器( ApplicationListener )
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
利用 Spring 工廠加載機制,實例化 ApplicationListene r實現類,並排序對象集合,具體方法跟上面 初始化Initializers
相似,不贅述。
# Application Listeners org.springframework.context.ApplicationListener=\ #Spring應用上下文加載完成以後清除緩存 org.springframework.boot.ClearCachesApplicationListener,\ #父容器關閉時通知各個子容器關閉, org.springframework.boot.builder.ParentContextCloserApplicationListener,\ #文件編碼 org.springframework.boot.context.FileEncodingApplicationListener,\ #控制檯彩色輸出 org.springframework.boot.context.config.AnsiOutputApplicationListener,\ #外部化配置 管理factories或者YMAL文件 org.springframework.boot.context.config.ConfigFileApplicationListener,\ #將指定事件廣播給指定的監聽器 org.springframework.boot.context.config.DelegatingApplicationListener,\ #將須要輸出的日誌打印到指定的級別 DEBUG INFO ERROR org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\ #初始化日誌系統 org.springframework.boot.context.logging.LoggingApplicationListener,\ #控制可執行Spirng文件版本 org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
Step4. 推斷引導類
private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { //根據 Main 線程執行堆棧來判斷實際的引導類。 if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; }
整個 SpringApplication 圍繞着 run 這個方法並分爲兩個小階段:
SpringApplication
運行監聽器,並監聽 Spring Boot
事件public ConfigurableApplicationContext run(String... args) { //記錄運行時間 StopWatch stopWatch = new StopWatch(); stopWatch.start(); //Spring 應用的上下文 ConfigurableApplicationContext context = null; //記錄啓動期間的錯誤 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); //配置文件加載及優先級判斷 configureHeadlessProperty(); //獲取SpringApplicationRunListeners SpringApplicationRunListeners listeners = getRunListeners(args); //加載運行監聽器 listeners.starting(); try { //建立ApplicationArguments對象 ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); //加載屬性配置 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); //打印Banner Banner printedBanner = printBanner(environment); //建立應用上下文 context = createApplicationContext(); //實例化SpringBootExceptionReporter用於報告啓動過程錯誤。 exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //初始化應用上下文 prepareContext(context, environment, listeners, applicationArguments, printedBanner); //刷新應用上下文(IOC容器的準備,初始化Bean) refreshContext(context); //應用上下刷新完成以後 afterRefresh(context, applicationArguments); stopWatch.stop(); //啓動日誌記錄器 if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } //啓動運行監聽器 listeners.started(context); //啓動後須要的操做 callRunners(context, applicationArguments); .... } }
Step1. 加載SpringApplication
運行監聽器
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)); }
利用 Spirng 的工廠加載機制,實例化 SpringApplicationRunListeners 實現類,並排序集合。具體實現方法以下:
SpringFactoriesLoader.loadFactoryNames
來掃描 META-INF/spring.factories
下符合 SpringApplicationRunListeners 類型的資源名稱。META-INF/spring.factories
下找到的資源信息# Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListenr
因而可知就只有 EventPublishingRunListenr
一個實現類
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { private final SpringApplication application; private final String[] args; private final SimpleApplicationEventMulticaster initialMulticaster; public EventPublishingRunListener(SpringApplication application, String[] args) { this.application = application; this.args = args; //實例化SimpleApplicationEventMulticaster事件發佈者 this.initialMulticaster = new SimpleApplicationEventMulticaster(); //以迭代的方法逐一進行ApplicationListener的監聽 for (ApplicationListener<?> listener : application.getListeners()) { this.initialMulticaster.addApplicationListener(listener); } } @Override public int getOrder() { return 0; } @Override public void starting() { this.initialMulticaster.multicastEvent( new ApplicationStartingEvent(this.application, this.args)); } @Override public void environmentPrepared(ConfigurableEnvironment environment) { this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent( this.application, this.args, environment)); } @Override public void contextPrepared(ConfigurableApplicationContext context) { } @Override public void contextLoaded(ConfigurableApplicationContext context) { for (ApplicationListener<?> listener : this.application.getListeners()) { if (listener instanceof ApplicationContextAware) { ((ApplicationContextAware) listener).setApplicationContext(context); } context.addApplicationListener(listener); } this.initialMulticaster.multicastEvent( new ApplicationPreparedEvent(this.application, this.args, context)); } @Override public void started(ConfigurableApplicationContext context) { context.publishEvent( new ApplicationStartedEvent(this.application, this.args, context)); } @Override public void running(ConfigurableApplicationContext context) { context.publishEvent( new ApplicationReadyEvent(this.application, this.args, context)); } @Override public void failed(ConfigurableApplicationContext context, Throwable exception) { ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception); if (context != null && context.isActive()) { // Listeners have been registered to the application context so we should // use it at this point if we can context.publishEvent(event); } else { // An inactive context may not have a multicaster so we use our multicaster to // call all of the context's listeners instead if (context instanceof AbstractApplicationContext) { for (ApplicationListener<?> listener : ((AbstractApplicationContext) context) .getApplicationListeners()) { this.initialMulticaster.addApplicationListener(listener); } } this.initialMulticaster.setErrorHandler(new LoggingErrorHandler()); this.initialMulticaster.multicastEvent(event); } } private static class LoggingErrorHandler implements ErrorHandler { private static Log logger = LogFactory.getLog(EventPublishingRunListener.class); @Override public void handleError(Throwable throwable) { logger.warn("Error calling ApplicationEventListener", throwable); } } }
在 EventPublishingRunListener
實例化的時候,會實例化一個 SimpleApplicationEventMulticaster
事件發佈者(它的做用就是監聽容器中發佈的事件,只要事件發生,就觸發監聽器的回調,來完成事件驅動開發),因而接下來調用 listeners.starting()
方法就會經過其內部的 initialMulticaster
屬性發布 ApplicationStartingEvent
事件。
Step2. 建立Spirng應用上下文
protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { //初始化階段的推斷Web類型 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); }
根據初始化階段的推斷Web應用類型來建立對應的 ConfigurableApplicationContext 實例
若是推斷的爲 SERVLETWeb 類型就實例化這個對象
web.servlet.context.AnnotationConfigServletWebServerApplicationContext
Step3. 建立 Environment
private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment //建立 ConfigurableEnvironment 對象 ConfigurableEnvironment environment = getOrCreateEnvironment(); //配置 ConfigurableEnvironment configureEnvironment(environment, applicationArguments.getSourceArgs()); //發佈 ApplicationEnvironmentPreparedEvent 事件 listeners.environmentPrepared(environment); //將 ConfigurableEnvironment 綁定到 SpringApplication 中 bindToSpringApplication(environment); if (this.webApplicationType == WebApplicationType.NONE) { environment = new EnvironmentConverter(getClassLoader()) .convertToStandardEnvironmentIfNecessary(environment); } ConfigurationPropertySources.attach(environment); return environment; }
根據初始化階段的推斷Web應用類型來建立對應的 ConfigurableEnvironment 實例。
至此整一個的 SpringBoot 過程已經分析完畢:咱們來總結一下:
若是本文對你哪怕有一丁點幫助,請幫忙點好看。你的好看是我堅持寫做的動力。
另外,關注以後在發送 1024 可領取免費學習資料。
資料詳情請看這篇舊文:Python、C++、Java、Linux、Go、前端、算法資料分享