Spring Boot啟動流程


Spring Boot啟動流程

 

        君生我未生,君生我已老。君恨我生遲,我恨君生早。

 

 一、簡述

Spring Boot啟動流程分析使用版本SpringBoot VERSION:版本 2.5.5-SNAPSHOT。

Spring Boot項目最簡單的Application啟動類。

可以看出Application啟動類中,包含了@SpringBootApplication 注解和 SpringApplication.run 啟動方法,所以SpringBoot的啟動可以分解為 注解啟動方法 兩大過程,而仔細看啟動類中還引入了一個【org.springframework.boot.SpringApplication】包,所以啟動方法中又可以分為兩個階段即 創建SpringApplication 實例執行run方法

二、注解

注解暫且簡單了解,暫不深入。

1、@SpirngBootApplication注解

進入@SpringBootApplication注解內。

從@SpringBootApplication注解內部可以發現,它雖然定義使用了多個Annotation進行了原信息標注,但實際上重要的只有三個Annotation:

  • @SpringBootConfiguration(@SpringBootConfiguration注解點開查看發現里面還是應用了@Configuration)->Spring IOC容器配置類。
  • @EnableAutoConfiguration ->使用@Import將所有符合自動配置條件的bean定義加載到IOC容器。
  • @ComponentScan ->自動掃描並加載符合條件的組件或者bean定義,默認掃描SpringApplication的run方法里的class所在的包路徑下文件,所以通常將該啟動類放到根包路徑下。

即 @SpringBootApplication = (默認屬性)@Configuration + @EnableAutoConfiguration + @ComponentScan。

三、啟動方法

啟動方法中分為兩個階段即 創建SpringApplication 實例執行run方法

1、創建SpringApplication實例

從啟動類中的run方法跟進去,SpringApplication.run -> return run -> return new SpringApplication(primarySources).run(args) -> this(null, primarySources) -> SpringApplication

其中:return new SpringApplication(primarySources).run(args) ,如果跟new SpringApplication(primarySources) 方法則是啟動方法中的第一階段即創建SpringApplication實例,跟run(args) 方法進去就是啟動方法中的第二階段。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources)
 1 /**
 2  * Create a new {@link SpringApplication} instance. The application context will load  3  * beans from the specified primary sources (see {@link SpringApplication class-level}  4  * documentation for details. The instance can be customized before calling  5  * {@link #run(String...)}.  6  *  7  * @param resourceLoader the resource loader to use  8  * @param primarySources the primary bean sources  9  * @see #run(Class, String[]) 10  * @see #setSources(Set) 11      */
12     @SuppressWarnings({"unchecked", "rawtypes"}) 13     public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { 14         // 初始化類加載器
15         this.resourceLoader = resourceLoader; 16         // Assert 斷言非空,若傳入的class參數為null則打印異常並退出初始化
17         Assert.notNull(primarySources, "PrimarySources must not be null"); 18         // 獲取main方法中的args,初始化啟動時配置的額外參數集合
19         this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); 20         // 判斷項目啟動類型:NONE/SERVLET/REACTIVE
21         this.webApplicationType = WebApplicationType.deduceFromClasspath(); 22         // 從 Spring 工廠獲取 Bootstrap Registry Initializers
23         this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories(); 24         // 獲取 Spring 工廠實例 -> 容器上下文相關的初始化
25         setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); 26         // 獲取 Spring 工廠實例 -> 設置應用程序監聽器
27         setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 28         // 推導出主應用程序類,即從當前的棧信息中尋找main所在主類:com.iot.SpringBootLoveApplication
29         this.mainApplicationClass = deduceMainApplicationClass(); 30     }
View Code

1.1、WebApplicationType

WebApplicationType 判斷項目類型。

 public enum WebApplicationType

 1 /*
 2  * Copyright 2012-2019 the original author or authors.  3  *  4  * Licensed under the Apache License, Version 2.0 (the "License");  5  * you may not use this file except in compliance with the License.  6  * You may obtain a copy of the License at  7  *  8  * https://www.apache.org/licenses/LICENSE-2.0
 9  *  10  * Unless required by applicable law or agreed to in writing, software  11  * distributed under the License is distributed on an "AS IS" BASIS,  12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  13  * See the License for the specific language governing permissions and  14  * limitations under the License.  15  */
 16 
 17 package org.springframework.boot;  18 
 19 import org.springframework.util.ClassUtils;  20 
 21 /**
 22  * An enumeration of possible types of web application.  23  *  24  * @author Andy Wilkinson  25  * @author Brian Clozel  26  * @since 2.0.0  27  */
 28 public enum WebApplicationType {  29 
 30     /**
 31  * The application should not run as a web application and should not start an  32  * embedded web server.  33      */
 34  NONE,  35 
 36     /**
 37  * The application should run as a servlet-based web application and should start an  38  * embedded servlet web server.  39      */
 40  SERVLET,  41 
 42     /**
 43  * The application should run as a reactive web application and should start an  44  * embedded reactive web server.  45      */
 46  REACTIVE;  47 
 48     private static final String[] SERVLET_INDICATOR_CLASSES = {"javax.servlet.Servlet",  49             "org.springframework.web.context.ConfigurableWebApplicationContext"};  50 
 51     private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";  52 
 53     private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";  54 
 55     private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";  56 
 57     private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";  58 
 59     private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";  60 
 61     /**
 62  * deduceFromClasspath  63  * 依次循環遍歷當前應用中是否存在相關的類來判斷最終應用的啟動類型  64  *  65  * @return
 66      */
 67     static WebApplicationType deduceFromClasspath() {  68         /**
 69  * REACTIVE:響應式WEB項目  70  * 若啟動類型為REACTIVE,  71  * 則類路徑下存在 org.springframework.web.reactive.DispatcherHandler 類  72  * 並且不存在 org.springframework.web.servlet.DispatcherServlet 和 org.glassfish.jersey.servlet.ServletContainer  73  * 兩者指的是SpringMVC/Tomcat和jersey容器  74          */
 75         if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)  76                 && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {  77             return WebApplicationType.REACTIVE;  78  }  79         /**
 80  * NONE:非WEB項目,就是一個最簡單的Springboot應用  81  * 若啟動類型為NONE  82  * 則類路徑下 javax.servlet.Servlet 和org.springframework.web.context.ConfigurableWebApplicationContext都不存在  83          */
 84         for (String className : SERVLET_INDICATOR_CLASSES) {  85             if (!ClassUtils.isPresent(className, null)) {  86                 return WebApplicationType.NONE;  87  }  88  }  89         /**
 90  * SERVLET:SERVLET WEB 項目  91  * 若啟動類型為Servlet,則必須有SERVLET_INDICATOR_CLASSES中的javax.servlet.Servlet  92  * 和org.springframework.web.context.ConfigurableWebApplicationContext  93          */
 94         return WebApplicationType.SERVLET;  95  }  96 
 97     static WebApplicationType deduceFromApplicationContext(Class<?> applicationContextClass) {  98         if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {  99             return WebApplicationType.SERVLET; 100  } 101         if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) { 102             return WebApplicationType.REACTIVE; 103  } 104         return WebApplicationType.NONE; 105  } 106 
107     private static boolean isAssignable(String target, Class<?> type) { 108         try { 109             return ClassUtils.resolveClassName(target, null).isAssignableFrom(type); 110         } catch (Throwable ex) { 111             return false; 112  } 113  } 114 
115 }
View Code

1.2、getBootstrapRegistryInitializersFromSpringFactories

getBootstrapRegistryInitializersFromSpringFactories方法從spring.factories 中獲取 BootstrapRegistryInitializer。

private List<BootstrapRegistryInitializer> getBootstrapRegistryInitializersFromSpringFactories() 

 1 private List<BootstrapRegistryInitializer> getBootstrapRegistryInitializersFromSpringFactories(){  2         ArrayList<BootstrapRegistryInitializer> initializers=new ArrayList<>();  3         /**
 4  * 從spring.factories 中獲取Bootstrapper集合,  5  * 然后遍歷轉化為BootstrapRegistryInitializer,再存入 initializers  6          */
 7         getSpringFactoriesInstances(Bootstrapper.class).stream()  8         .map((bootstrapper)->((BootstrapRegistryInitializer)bootstrapper::initialize))  9  .forEach(initializers::add); 10         /**
11  * 從spring.factories 中獲取BootstrapRegistryInitializer集合,再存入 initializers 12  * getSpringFactoriesInstances 該方法在整個啟動流程中會頻繁出現,下面集中介紹 13          */
14         initializers.addAll(getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); 15         return initializers; 16         }
View Code

1.3、setInitializers && setListeners

setInitializers && setListeners 分別是容器上下文初始化 & 監聽器初始化。

容器上下文初始化setInitializers 和監聽器初始化setListeners 都是調用了getSpringFactoriesInstances() 方法,從spring.factories中獲取配置。不同的是傳給它的type參數,主要有一下幾種類型。

  • ApplicationContextInitializer.class 上下文相關
  • ApplicationListener.class 監聽器相關
  • SpringApplicationRunListener.class 運行時監聽器
  • SpringBootExceptionReporter.class 異常類相關

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args)
 1 /**
 2  * The location to look for factories.  3  * <p>Can be present in multiple JAR files.  4      */
 5     public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";  6 
 7 
 8     /**
 9  * 從spring.factories中獲取配置  10      */
 11     private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {  12         ClassLoader classLoader = getClassLoader();  13         // Use names and ensure unique to protect against duplicates
 14         /**
 15  * 加載各jar包中的"META-INF/spring.factories"配置  16  * 其中SpringFactoriesLoader.loadFactoryNames(type, classLoader) 方法  17  * 是獲取spring.factories配置文件中已經配置的指定類型的的實現類集合  18  * 其中FACTORIES_RESOURCE_LOCATION的值:META-INF/spring.factories  19          */
 20         Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));  21         // 通過反射創建這些類
 22         List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);  23         // 排序
 24  AnnotationAwareOrderComparator.sort(instances);  25         return instances;  26  }  27 
 28 
 29     /**
 30  * Load the fully qualified class names of factory implementations of the  31  * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given  32  * class loader.  33  * <p>As of Spring Framework 5.3, if a particular implementation class name  34  * is discovered more than once for the given factory type, duplicates will  35  * be ignored.  36  *  37  * @param factoryType the interface or abstract class representing the factory  38  * @param classLoader the ClassLoader to use for loading resources; can be  39  * {@code null} to use the default  40  * @throws IllegalArgumentException if an error occurs while loading factory names  41  * @see #loadFactories  42      */
 43     public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {  44         ClassLoader classLoaderToUse = classLoader;  45         if (classLoaderToUse == null) {  46             classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();  47  }  48         String factoryTypeName = factoryType.getName();  49         return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());  50  }  51 
 52 
 53     /**
 54  * Springboot自動配置的秘密  55  * Springboot在啟動時讀取了所有starter jar包里的META-INF/spring.factories配置文件,實現了所謂的自動化配置  56  * 這里jar包里的都是默認配置,后續Springboot也會從xml、yaml文件中的用戶配置去覆蓋同名的配置。  57  * 另外,這里的緩存配置是保存在一個map類型的cache中,其中的key鍵對應上面提到的各種Type類型,value就是Type的各種初始jar包里的同類型Java類。  58      */
 59     private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {  60         // 獲取相應類加載器中內容
 61         Map<String, List<String>> result = cache.get(classLoader);  62         // 存在則直接返回類加載器中內容
 63         if (result != null) {  64             return result;  65  }  66         // 不存在則初始化類加載器中內容
 67         result = new HashMap<>();  68         try {  69             /**
 70  * 獲取資源 -> META-INF/spring.factories 列表  71  * 其中FACTORIES_RESOURCE_LOCATION的值:META-INF/spring.factories  72              */
 73             Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);  74             // 可能存在多個META-INF/spring.factories 文件,循環加載
 75             while (urls.hasMoreElements()) {  76                 // 獲取 META-INF/spring.factories 文件URL地址
 77                 URL url = urls.nextElement();  78                 // 加載資源
 79                 UrlResource resource = new UrlResource(url);  80                 // 加載資源配置
 81                 Properties properties = PropertiesLoaderUtils.loadProperties(resource);  82                 // key:value形式循環配置
 83                 for (Map.Entry<?, ?> entry : properties.entrySet()) {  84                     String factoryTypeName = ((String) entry.getKey()).trim();  85                     // 逗號分隔列表到字符串數組
 86                     String[] factoryImplementationNames =
 87  StringUtils.commaDelimitedListToStringArray((String) entry.getValue());  88                     // 循環value中子項到列表中
 89                     for (String factoryImplementationName : factoryImplementationNames) {  90                         result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())  91  .add(factoryImplementationName.trim());  92  }  93  }  94  }  95 
 96             // Replace all lists with unmodifiable lists containing unique elements  97             // 列表去重
 98             result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()  99  .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList))); 100             // 列表保存
101  cache.put(classLoader, result); 102         } catch (IOException ex) { 103             throw new IllegalArgumentException("Unable to load factories from location [" +
104                     FACTORIES_RESOURCE_LOCATION + "]", ex); 105  } 106         return result; 107  } 108 
109 
110     /**
111  * 反射創建實現類 112      */
113     private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, 114                                                        ClassLoader classLoader, Object[] args, Set<String> names) { 115         List<T> instances = new ArrayList<>(names.size()); 116         for (String name : names) { 117             try { 118                 Class<?> instanceClass = ClassUtils.forName(name, classLoader); 119  Assert.isAssignable(type, instanceClass); 120                 Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes); 121                 T instance = (T) BeanUtils.instantiateClass(constructor, args); 122  instances.add(instance); 123             } catch (Throwable ex) { 124                 throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex); 125  } 126  } 127         return instances; 128     }
View Code

1.4、deduceMainApplicationClass

deduceMainApplicationClass 推導主應用程序類。

private Class<?> deduceMainApplicationClass()
 1  /**
 2  * 推導主應用程序類  3  * @return
 4      */
 5     private Class<?> deduceMainApplicationClass() {  6         try {  7             // 獲取當前的棧信息
 8             StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();  9             for (StackTraceElement stackTraceElement : stackTrace) { 10                 // 獲取main方法所在的類class,此處即com.iot.SpringBootLoveApplication
11                 if ("main".equals(stackTraceElement.getMethodName())) { 12                     return Class.forName(stackTraceElement.getClassName()); 13  } 14  } 15  } 16         catch (ClassNotFoundException ex) { 17             // Swallow and continue
18  } 19         return null; 20     }
View Code

2、run方法

初始化完SpringApplication 就可以運行他的run方法了,也就是啟動方法中的第二階段。

public ConfigurableApplicationContext run(String... args)
 1 /**
 2  * Run the Spring application, creating and refreshing a new  3  * {@link ApplicationContext}.  4  *  5  * @param args the application arguments (usually passed from a Java main method)  6  * @return a running {@link ApplicationContext}  7      */
 8     public ConfigurableApplicationContext run(String... args) {  9         // 啟動一個秒表計時器,用於統計項目啟動時間
10         StopWatch stopWatch = new StopWatch(); 11  stopWatch.start(); 12         // 創建啟動上下文對象即spring根容器
13         DefaultBootstrapContext bootstrapContext = createBootstrapContext(); 14         // 定義可配置的應用程序上下文變量
15         ConfigurableApplicationContext context = null; 16         /**
17  * 設置jdk系統屬性 18  * headless直譯就是無頭模式, 19  * headless模式的意思就是明確Springboot要在無鼠鍵支持的環境中運行,一般程序也都跑在Linux之類的服務器上,無鼠鍵支持,這里默認值是true; 20          */
21  configureHeadlessProperty(); 22         /**
23  * 獲取運行監聽器 getRunListeners, 其中也是調用了上面說到的getSpringFactoriesInstances 方法 24  * 從spring.factories中獲取配置 25          */
26         SpringApplicationRunListeners listeners = getRunListeners(args); 27         // 啟動監聽器
28         listeners.starting(bootstrapContext, this.mainApplicationClass); 29         try { 30             // 包裝默認應用程序參數,也就是在命令行下啟動應用帶的參數,如--server.port=9000
31             ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); 32             // 33             /**
34  * 准備環境 prepareEnvironment 是個硬茬,里面主要涉及到 35  * getOrCreateEnvironment、configureEnvironment、configurePropertySources、configureProfiles 36  * environmentPrepared、bindToSpringApplication、attach諸多方法可以在下面的例子中查看 37              */
38             ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); 39             // 配置忽略的 bean
40  configureIgnoreBeanInfo(environment); 41             // 打印 SpringBoot 標志,即啟動的時候在控制台的圖案logo,可以在src/main/resources下放入名字是banner的自定義文件
42             Banner printedBanner = printBanner(environment); 43             // 創建 IOC 容器
44             context = createApplicationContext(); 45             // 設置一個啟動器,設置應用程序啟動
46             context.setApplicationStartup(this.applicationStartup); 47             // 配置 IOC 容器的基本信息 (spring容器前置處理)
48  prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); 49             /**
50  * 刷新IOC容器 51  * 這里會涉及Spring容器啟動、自動裝配、創建 WebServer啟動Web服務即SpringBoot啟動內嵌的 Tomcat 52              */
53  refreshContext(context); 54             /**
55  * 留給用戶自定義容器刷新完成后的處理邏輯 56  * 刷新容器后的擴展接口(spring容器后置處理) 57              */
58  afterRefresh(context, applicationArguments); 59             // 結束計時器並打印,這就是我們啟動后console的顯示的時間
60  stopWatch.stop(); 61             if (this.logStartupInfo) { 62                 // 打印啟動完畢的那行日志
63                 new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); 64  } 65             // 發布監聽應用上下文啟動完成(發出啟動結束事件),所有的運行監聽器調用 started() 方法
66  listeners.started(context); 67             // 執行runner,遍歷所有的 runner,調用 run 方法
68  callRunners(context, applicationArguments); 69         } catch (Throwable ex) { 70             // 異常處理,如果run過程發生異常
71  handleRunFailure(context, ex, listeners); 72             throw new IllegalStateException(ex); 73  } 74 
75         try { 76             // 所有的運行監聽器調用 running() 方法,監聽應用上下文
77  listeners.running(context); 78         } catch (Throwable ex) { 79             // 異常處理
80             handleRunFailure(context, ex, null); 81             throw new IllegalStateException(ex); 82  } 83         // 返回最終構建的容器對象
84         return context; 85     }

View Code

2.1、configureHeadlessProperty

configureHeadlessProperty 設置headless無頭模式。

private void configureHeadlessProperty()
 1     private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";  2     
 3     /**
 4  * headless直譯就是無頭模式,  5  * headless模式的意思就是明確Springboot要在無鼠鍵支持的環境中運行,一般程序也都跑在Linux之類的服務器上,無鼠鍵支持,這里默認值是true;  6      */
 7     private void configureHeadlessProperty() {  8         // SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
 9  System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, 10                 System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless))); 11     }

2.2、prepareEnvironment

prepareEnvironment 准備環境是個硬茬,里面主要涉及到getOrCreateEnvironment、configureEnvironment、configurePropertySources、configureProfilesenvironmentPrepared、bindToSpringApplication、attach諸多方法。

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) 

 1     /**
 2  * 准備環境  3  *  4  * @param listeners  5  * @param bootstrapContext  6  * @param applicationArguments  7  * @return
 8      */
 9     private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,  10  DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {  11         // Create and configure the environment 創建和配置環境  12         // 根據項目類型建環境ConfigurableEnvironment
 13         ConfigurableEnvironment environment = getOrCreateEnvironment();  14         // 從環境中獲取並設置 PropertySources 和 activeProfiles
 15  configureEnvironment(environment, applicationArguments.getSourceArgs());  16         // 把 PropertySources 設置在自己PropertySources的第一個位置
 17  ConfigurationPropertySources.attach(environment);  18         /**
 19  * 運行監聽器調用  20  * 廣播事件,listeners環境准備(就是廣播ApplicationEnvironmentPreparedEvent事件)  21  * 發布事件通知所有的監聽器當前環境准備完成  22          */
 23  listeners.environmentPrepared(bootstrapContext, environment);  24         // 移動 defaultProperties 屬性源到環境中的最后一個源
 25  DefaultPropertiesPropertySource.moveToEnd(environment);  26         // 斷言 拋異常
 27         Assert.state(!environment.containsProperty("spring.main.environment-prefix"),  28                 "Environment prefix cannot be set via properties.");  29         // 與容器綁定當前環境
 30  bindToSpringApplication(environment);  31         // 若非web環境,將環境轉換成StandardEnvironment
 32         if (!this.isCustomEnvironment) {  33             environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,  34  deduceEnvironmentClass());  35  }  36         // 配置PropertySources對它自己的遞歸依賴
 37  ConfigurationPropertySources.attach(environment);  38         return environment;  39  }  40 
 41 
 42     /**
 43  * 獲取或創建環境Environment  44  *  45  * @return
 46      */
 47     private ConfigurableEnvironment getOrCreateEnvironment() {  48         // 存在則直接返回
 49         if (this.environment != null) {  50             return this.environment;  51  }  52         /**
 53  * 根據webApplicationType創建對應的Environment  54          */
 55         switch (this.webApplicationType) {  56             // SERVLET WEB 項目
 57             case SERVLET:  58                 return new ApplicationServletEnvironment();  59             // REACTIVE:響應式WEB項目
 60             case REACTIVE:  61                 return new ApplicationReactiveWebEnvironment();  62             // 非WEB項目,就是一個最簡單的Springboot應用
 63             default:  64                 return new ApplicationEnvironment();  65  }  66  }  67 
 68     /**
 69  * 從環境中獲取並設置 PropertySources 和 activeProfiles  70  * 將配置任務按順序委托給configurePropertySources和configureProfiles  71  * Template method delegating to  72  * {@link #configurePropertySources(ConfigurableEnvironment, String[])} and  73  * {@link #configureProfiles(ConfigurableEnvironment, String[])} in that order.  74  * Override this method for complete control over Environment customization, or one of  75  * the above for fine-grained control over property sources or profiles, respectively.  76  *  77  * @param environment this application's environment  78  * @param args arguments passed to the {@code run} method  79  * @see #configureProfiles(ConfigurableEnvironment, String[])  80  * @see #configurePropertySources(ConfigurableEnvironment, String[])  81      */
 82     protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {  83         if (this.addConversionService) {  84             environment.setConversionService(new ApplicationConversionService());  85  }  86         // 配置PropertySources
 87  configurePropertySources(environment, args);  88         // 配置Profiles
 89  configureProfiles(environment, args);  90  }  91 
 92     /**
 93  * 配置PropertySources  94  * Add, remove or re-order any {@link PropertySource}s in this application's  95  * environment.  96  *  97  * @param environment this application's environment  98  * @param args arguments passed to the {@code run} method  99  * @see #configureEnvironment(ConfigurableEnvironment, String[]) 100      */
101     protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) { 102         MutablePropertySources sources = environment.getPropertySources(); 103         // 初始化 defaultProperties
104         if (!CollectionUtils.isEmpty(this.defaultProperties)) { 105             // 存在的話將其放到最后位置
106             DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources); 107  } 108         /**
109  * 存在命令行參數,則解析它並封裝進SimpleCommandLinePropertySource對象 110  * 同時將此對象放到sources的第一位置(優先級最高) 111          */
112         if (this.addCommandLineProperties && args.length > 0) { 113             String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; 114             if (sources.contains(name)) { 115                 PropertySource<?> source = sources.get(name); 116                 CompositePropertySource composite = new CompositePropertySource(name); 117  composite.addPropertySource( 118                         new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args)); 119  composite.addPropertySource(source); 120  sources.replace(name, composite); 121             } else { 122                 // 放到首位
123                 sources.addFirst(new SimpleCommandLinePropertySource(args)); 124  } 125  } 126  } 127 
128     /**
129  * 配置Profiles 130  * 131  * @param environment 132  * @param args 133      */
134     protected void configureProfiles(ConfigurableEnvironment environment, String[] args) { 135         /**
136  * 保證environment的activeProfiles屬性被初始化了。從PropertySources中查找spring.profiles.active屬性 137  * 存在則將其值添加activeProfiles集合中。 138  * 配置應用環境中的哪些配置文件處於激活狀態(或默認激活) 139  * 可以通過spring.profiles.active屬性在配置文件處理期間激活其他配置文件 140  * 就是我們項目中通常配置的dev、sit、prod等環境配置信息設置哪些Profiles是激活的。 141          */
142         environment.getActiveProfiles(); // ensure they are initialized 143         // But these ones should go first (last wins in a property key clash) 144         // 如果存在其他的Profiles,則將這些Profiles放到第一的位置
145         Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles); 146  profiles.addAll(Arrays.asList(environment.getActiveProfiles())); 147  environment.setActiveProfiles(StringUtils.toStringArray(profiles)); 148  } 149 
150     /**
151  * 運行監聽器調用 152  * 153  * @param bootstrapContext 154  * @param environment 155      */
156     void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { 157         doWithListeners("spring.boot.application.environment-prepared", 158                 (listener) -> listener.environmentPrepared(bootstrapContext, environment)); 159  } 160 
161     /**
162  * 運行監聽器調用 163  * Called once the environment has been prepared, but before the 164  * {@link ApplicationContext} has been created. 165  * 166  * @param environment the environment 167  * @deprecated since 2.4.0 for removal in 2.6.0 in favor of 168  * {@link #environmentPrepared(ConfigurableBootstrapContext, ConfigurableEnvironment)} 169      */
170  @Deprecated 171     default void environmentPrepared(ConfigurableEnvironment environment) { 172         for (SpringApplicationRunListener listener : this.listeners) { 173             // 廣播ApplicationEnvironmentPreparedEvent事件,后面再看
174  listener.environmentPrepared(environment); 175  } 176  } 177 
178     /**
179  * 與容器綁定當前環境 180  * Bind the environment to the {@link SpringApplication}. 181  * 182  * @param environment the environment to bind 183      */
184     protected void bindToSpringApplication(ConfigurableEnvironment environment) { 185         try { 186             // 將environment綁定到SpringApplication
187             Binder.get(environment).bind("spring.main", Bindable.ofInstance(this)); 188         } catch (Exception ex) { 189             throw new IllegalStateException("Cannot bind to SpringApplication", ex); 190  } 191  } 192 
193     /**
194  * 配置PropertySources對它自己的遞歸依賴 195  * Attach a {@link ConfigurationPropertySource} support to the specified 196  * {@link Environment}. Adapts each {@link PropertySource} managed by the environment 197  * to a {@link ConfigurationPropertySource} and allows classic 198  * {@link PropertySourcesPropertyResolver} calls to resolve using 199  * {@link ConfigurationPropertyName configuration property names}. 200  * <p> 201  * The attached resolver will dynamically track any additions or removals from the 202  * underlying {@link Environment} property sources. 203  * 204  * @param environment the source environment (must be an instance of 205  * {@link ConfigurableEnvironment}) 206  * @see #get(Environment) 207      */
208     public static void attach(Environment environment) { 209         // 判斷environment是否是ConfigurableEnvironment的實例
210         Assert.isInstanceOf(ConfigurableEnvironment.class, environment); 211         // 從environment獲取PropertySources
212         MutablePropertySources sources = ((ConfigurableEnvironment) environment) 213  .getPropertySources(); 214         PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME); 215         if (attached != null && attached.getSource() != sources) { 216  sources.remove(ATTACHED_PROPERTY_SOURCE_NAME); 217             attached = null; 218  } 219         if (attached == null) { 220             // 將sources封裝成ConfigurationPropertySourcesPropertySource對象,並把這個對象放到sources的第一位置
221             sources.addFirst(new ConfigurationPropertySourcesPropertySource( 222  ATTACHED_PROPERTY_SOURCE_NAME, 223                     new SpringConfigurationPropertySources(sources))); 224  } 225     }
View Code

2.3、printBanner

printBanner 打印SpringBoot標志。printBanner(environment)方法就是打印Banner,Banner就是項目啟動時看到的那個logo。在工程項目src/main/resources路徑下下放入名字是banner的文件,后綴后可以是SpringApplicationBannerPrinter.java類里的{ "gif", "jpg", "png" },或者是txt、圖片也可以的,但是圖片打印時會字符化,而不是打印圖片本身。自定義banner鏈接

 private Banner printBanner(ConfigurableEnvironment environment)

 1     /**
 2      * 打印SpringBoot標志
 3      * banner的輸出默認有三種種模式,LOG、CONSOLE、OFF。
 4      * 1. LOG:將banner信息輸出到日志文件。
 5      * 2. CONSOLE:將banner信息輸出到控制台。
 6      * 3. OFF:禁用banner的信息輸出。
 7      *
 8      * @param environment
 9      * @return
10      */
11     private Banner printBanner(ConfigurableEnvironment environment) {
12         // 判斷Banner的模式是否關閉,如果關閉直接返回。
13         if (this.bannerMode == Banner.Mode.OFF) {
14             return null;
15         }
16         ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
17                 : new DefaultResourceLoader(null);
18         // 創建SpringApplicationBannerPrinter 打印類
19         SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
20         // LOG:將banner信息輸出到日志文件
21         if (this.bannerMode == Mode.LOG) {
22             return bannerPrinter.print(environment, this.mainApplicationClass, logger);
23         }
24         //banner沒有關閉且沒有指定是寫到log文件中 將banner信息輸出到控制台
25         return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
26     }
27 
28     /**
29      * 打印
30      *
31      * @param environment
32      * @param sourceClass
33      * @param logger
34      * @return
35      */
36     Banner print(Environment environment, Class<?> sourceClass, Log logger) {
37         // 獲取banner內容
38         Banner banner = getBanner(environment);
39         try {
40             logger.info(createStringFromBanner(banner, environment, sourceClass));
41         } catch (UnsupportedEncodingException ex) {
42             logger.warn("Failed to create String for banner", ex);
43         }
44         return new PrintedBanner(banner, sourceClass);
45     }
46 
47     /**
48      * 獲取banner內容
49      *
50      * @param environment
51      * @return
52      */
53     private Banner getBanner(Environment environment) {
54         Banners banners = new Banners();
55         // 圖片類型的banner內容
56         banners.addIfNotNull(getImageBanner(environment));
57         // 文本類型的banner內容
58         banners.addIfNotNull(getTextBanner(environment));
59         if (banners.hasAtLeastOneBanner()) {
60             return banners;
61         }
62         if (this.fallbackBanner != null) {
63             return this.fallbackBanner;
64         }
65         return DEFAULT_BANNER;
66     }
67 
68     static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
69     static final String DEFAULT_BANNER_LOCATION = "banner.txt";
70 
71     /**
72      * 文本類型的banner內容獲取
73      *
74      * @param environment
75      * @return
76      */
77     private Banner getTextBanner(Environment environment) {
78         /**
79          * 拿到自定義配置的banner文件地址
80          * BANNER_LOCATION_PROPERTY = "spring.banner.location"
81          * DEFAULT_BANNER_LOCATION = "banner.txt";
82          */
83         String location = environment.getProperty(BANNER_LOCATION_PROPERTY, DEFAULT_BANNER_LOCATION);
84         Resource resource = this.resourceLoader.getResource(location);
85         try {
86             if (resource.exists() && !resource.getURL().toExternalForm().contains("liquibase-core")) {
87                 return new ResourceBanner(resource);
88             }
89         } catch (IOException ex) {
90             // Ignore
91         }
92         return null;
93     }
View Code

2.4、createApplicationContext

createApplicationContext創建IOC容器。

protected ConfigurableApplicationContext createApplicationContext() 

 1     /**
 2  * 創建 IOC 容器  3  * A default {@link ApplicationContextFactory} implementation that will create an  4  * appropriate context for the {@link WebApplicationType}.  5      */
 6     ApplicationContextFactory DEFAULT = (webApplicationType) -> {  7         try {  8             // 根據當前應用的類型創建 IOC 容器
 9             switch (webApplicationType) { 10                 // Web 應用環境對應 AnnotationConfigServletWebServerApplicationContext
11                 case SERVLET: 12                     return new AnnotationConfigServletWebServerApplicationContext(); 13                 // 響應式編程對應 AnnotationConfigReactiveWebServerApplicationContext
14                 case REACTIVE: 15                     return new AnnotationConfigReactiveWebServerApplicationContext(); 16                 // 默認為 Spring 環境 AnnotationConfigApplicationContext
17                 default: 18                     return new AnnotationConfigApplicationContext(); 19  } 20  } 21         catch (Exception ex) { 22             throw new IllegalStateException("Unable create a default ApplicationContext instance, "
23                     + "you may need a custom ApplicationContextFactory", ex); 24  } 25  }; 26 
27     /**
28  * 設置一個啟動器 29  * Set the {@link ApplicationStartup} for this application context. 30  * <p>This allows the application context to record metrics 31  * during startup. 32  * @param applicationStartup the new context event factory 33  * @since 5.3 34      */
35     void setApplicationStartup(ApplicationStartup applicationStartup);
View Code

2.5、prepareContext

prepareContext 配置 IOC 容器的基本信息。

private void prepareContext(參數此處省略)

 1     /**
 2  * 准備IOC容器基本信息  3  * @param bootstrapContext  4  * @param context  5  * @param environment  6  * @param listeners  7  * @param applicationArguments  8  * @param printedBanner  9      */
10     private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, 11  ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, 12  ApplicationArguments applicationArguments, Banner printedBanner) { 13         // 設置容器環境,包括各種變量
14  context.setEnvironment(environment); 15         /**
16  * 后置處理流程 17  * 設置IOC容器的 bean 生成器和資源加載器 18          */
19  postProcessApplicationContext(context); 20         /**
21  * 獲取所有的初始化器調用 initialize() 方法進行初始化 22  * 執行容器中的ApplicationContextInitializer(包括從 spring.factories和自定義的實例)初始化 23          */
24  applyInitializers(context); 25         /**
26  * 觸發所有 SpringApplicationRunListener 監聽器的 contextPrepared 事件方法 27  * 所有的運行監聽器調用 environmentPrepared() 方法,EventPublishingRunListener 發布事件通知 IOC 容器准備完成 28          */
29  listeners.contextPrepared(context); 30  bootstrapContext.close(context); 31         // 打印啟動日志
32         if (this.logStartupInfo) { 33             logStartupInfo(context.getParent() == null); 34  logStartupProfileInfo(context); 35  } 36         // Add boot specific singleton beans
37         ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); 38         // 注冊添加特定的單例bean
39         beanFactory.registerSingleton("springApplicationArguments", applicationArguments); 40         if (printedBanner != null) { 41             beanFactory.registerSingleton("springBootBanner", printedBanner); 42  } 43         if (beanFactory instanceof DefaultListableBeanFactory) { 44  ((DefaultListableBeanFactory) beanFactory) 45                     .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); 46  } 47         if (this.lazyInitialization) { 48             context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); 49  } 50         // Load the sources 51         // 加載所有資源
52         Set<Object> sources = getAllSources(); 53         // 斷言資源費控
54         Assert.notEmpty(sources, "Sources must not be empty"); 55         // 創建BeanDefinitionLoader,加載啟動類,將啟動類注入容器
56         load(context, sources.toArray(new Object[0])); 57         // 觸發所有 SpringApplicationRunListener 監聽器的 contextLoaded 事件方法
58  listeners.contextLoaded(context); 59     }
View Code

2.6、refresh

refresh 刷新應用上下文,即刷新Spring上下文信息refreshContext。這里會涉及Spring容器啟動、SpringBoot自動裝配、創建 WebServer啟動Web服務即SpringBoot啟動內嵌的 Tomcat。

private void refreshContext(ConfigurableApplicationContext context) 

 1     /**
 2  * 刷新應用上下文  3  *  4  * @param context  5      */
 6     private void refreshContext(ConfigurableApplicationContext context) {  7         if (this.registerShutdownHook) {  8             // 判斷是否注冊關閉的鈎子,是則注冊鈎子
 9  shutdownHook.registerApplicationContext(context);  10  }  11  refresh(context);  12  }  13 
 14     /**
 15  * Refresh the underlying {@link ApplicationContext}.  16  *  17  * @param applicationContext the application context to refresh  18      */
 19     protected void refresh(ConfigurableApplicationContext applicationContext) {  20  applicationContext.refresh();  21  }  22 
 23     /**
 24  * 刷新IOC容器  25  *  26  * @throws BeansException  27  * @throws IllegalStateException  28      */
 29  @Override  30     public void refresh() throws BeansException, IllegalStateException {  31         synchronized (this.startupShutdownMonitor) {  32             StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");  33 
 34             // Prepare this context for refreshing. 准備刷新上下文
 35  prepareRefresh();  36 
 37             // Tell the subclass to refresh the internal bean factory. 通知子類刷新內部工廠
 38             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  39 
 40             // Prepare the bean factory for use in this context. 准備Bean工廠
 41  prepareBeanFactory(beanFactory);  42 
 43             try {  44                 // Allows post-processing of the bean factory in context subclasses.  45                 // 允許在上下文子類中對bean工廠進行后處理,這部分涉及Web服務器的啟動,如servlet
 46  postProcessBeanFactory(beanFactory);  47 
 48                 StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");  49                 // Invoke factory processors registered as beans in the context.  50                 // 調用在上下文中注冊為 bean 的工廠處理器
 51  invokeBeanFactoryPostProcessors(beanFactory);  52 
 53                 // Register bean processors that intercept bean creation. 注冊攔截 bean 創建的 bean 處理器
 54  registerBeanPostProcessors(beanFactory);  55  beanPostProcess.end();  56 
 57                 // Initialize message source for this context. 初始化此上下文的消息源
 58  initMessageSource();  59 
 60                 // Initialize event multicaster for this context. 為該上下文初始化事件多播器
 61  initApplicationEventMulticaster();  62 
 63                 // Initialize other special beans in specific context subclasses. 初始化特定上下文子類中的其他特殊 bean
 64                 /**
 65  * SpringBoot 一鍵啟動web工程的關鍵方法  66  * 創建 WebServer啟動Web服務  67  * SpringBoot啟動內嵌的 Tomcat 首先要在pom文件配置內嵌容器為tomcat  68  * SpringBoot 嵌入式 Servlet 容器,默認支持的 webServe:Tomcat、Jetty、Undertow  69  * <exclusion>  70  * <groupId>org.springframework.boot</groupId>  71  * <artifactId>spring-boot-starter-tomcat</artifactId>  72  * </exclusion>  73                  */
 74  onRefresh();  75 
 76                 // Check for listener beans and register them. 檢查偵聽器 bean 並注冊
 77  registerListeners();  78 
 79                 // Instantiate all remaining (non-lazy-init) singletons. 實例化所有剩余的(非延遲初始化)單例
 80  finishBeanFactoryInitialization(beanFactory);  81 
 82                 // Last step: publish corresponding event. 發布事件
 83  finishRefresh();  84             } catch (BeansException ex) {  85                 if (logger.isWarnEnabled()) {  86                     logger.warn("Exception encountered during context initialization - " +
 87                             "cancelling refresh attempt: " + ex);  88  }  89 
 90                 // Destroy already created singletons to avoid dangling resources. 銷毀bean
 91  destroyBeans();  92 
 93                 // Reset 'active' flag.
 94  cancelRefresh(ex);  95 
 96                 // Propagate exception to caller.
 97                 throw ex;  98             } finally {  99                 // Reset common introspection caches in Spring's core, since we 100                 // might not ever need metadata for singleton beans anymore...
101  resetCommonCaches(); 102  contextRefresh.end(); 103  } 104  } 105     }
View Code

2.7、onRefresh

onRefresh方法中創建WebServer、創建Tomcat對象,是SpringBoot一鍵啟動web工程的關鍵。SpringBoot 嵌入式 Servlet 容器,默認支持的 webServe:Tomcat、Jetty、Undertow,但要在POM文件加入tomcat相關配置。

 1 <dependency>
 2     <groupId>org.springframework.boot</groupId>
 3     <artifactId>spring-boot-starter-web</artifactId>
 4     <exclusions>
 5         <exclusion> <!--必須要把內嵌的 Tomcat 容器-->
 6             <groupId>org.springframework.boot</groupId>
 7             <artifactId>spring-boot-starter-tomcat</artifactId>
 8         </exclusion>
 9     </exclusions>
10 </dependency>
11 <dependency>
12     <groupId>org.springframework.boot</groupId>
13     <artifactId>spring-boot-starter-jetty</artifactId>
14 </dependency>
View Code

protected void onRefresh() throws BeansException 

 1     /**
 2  * 創建 WebServer啟動Web服務  3      */
 4  @Override  5     protected void onRefresh() {  6         // 初始化給定應用程序上下文的主題資源
 7         super.onRefresh();  8         try {  9             // 創建Web 服務
 10  createWebServer();  11  }  12         catch (Throwable ex) {  13             throw new ApplicationContextException("Unable to start web server", ex);  14  }  15  }  16 
 17     /**
 18  * super.onRefresh();  19  * Initialize the theme capability.  20      */
 21  @Override  22     protected void onRefresh() {  23         /**
 24  * 初始化給定應用程序上下文的主題資源,自動檢測一個名為“themeSource”的bean。  25  * 如果沒有這樣的,將使用默認的(空的)ThemeSource。  26          */
 27         this.themeSource = UiApplicationContextUtils.initThemeSource(this);  28  }  29 
 30     /**
 31  * 創建Web 服務  32      */
 33     private void createWebServer() {  34         WebServer webServer = this.webServer;  35         ServletContext servletContext = getServletContext();  36         if (webServer == null && servletContext == null) {  37             // 獲取web server
 38             StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");  39             // 獲取創建容器的工廠
 40             ServletWebServerFactory factory = getWebServerFactory();  41             createWebServer.tag("factory", factory.getClass().toString());  42             /**
 43  * 獲取 tomcat 、Jetty 或 Undertow 容器  44  * 從 getWebServer 方法點進去,找到 TomcatServletWebServerFactory 的實現方法,  45  * 與之對應的還有 Jetty 和 Undertow。這里配置了基本的連接器、引擎、虛擬站點等配置。  46  * 自動配置類 ServletWebServerFactoryAutoConfiguration 導入了 ServletWebServerFactoryConfiguration(配置類),  47  * 根據條件裝配判斷系統中到底導入了哪個 Web 服務器的包,創建出服務器並啟動  48  * 默認是 web-starter 導入 tomcat 包,容器中就有 TomcatServletWebServerFactory,創建出 Tomcat 服務器並啟動  49              */
 50             this.webServer = factory.getWebServer(getSelfInitializer());  51  createWebServer.end();  52             getBeanFactory().registerSingleton("webServerGracefulShutdown",  53                     new WebServerGracefulShutdownLifecycle(this.webServer));  54             getBeanFactory().registerSingleton("webServerStartStop",  55                     new WebServerStartStopLifecycle(this, this.webServer));  56  }  57         else if (servletContext != null) {  58             try {  59                 // 啟動web server
 60  getSelfInitializer().onStartup(servletContext);  61  }  62             catch (ServletException ex) {  63                 throw new ApplicationContextException("Cannot initialize servlet context", ex);  64  }  65  }  66  initPropertySources();  67  }  68 
 69     /**
 70  * 獲取tomcat 容器  71  * 配置了基本的連接器、引擎、虛擬站點等配置  72  * @param initializers  73  * @return
 74      */
 75  @Override  76     public WebServer getWebServer(ServletContextInitializer... initializers) {  77         if (this.disableMBeanRegistry) {  78  Registry.disableRegistry();  79  }  80         /**
 81  * 創建了Tomcat對象,並設置參數  82          */
 83         Tomcat tomcat = new Tomcat();  84         // 設置工作忙碌
 85         File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");  86  tomcat.setBaseDir(baseDir.getAbsolutePath());  87         // 初始化tomcat 連接,默認NIO
 88         Connector connector = new Connector(this.protocol);  89         connector.setThrowOnFailure(true);  90  tomcat.getService().addConnector(connector);  91  customizeConnector(connector);  92         // 配置基本的連接器、引擎、虛擬站點
 93  tomcat.setConnector(connector);  94         // 設置自動部署為false
 95         tomcat.getHost().setAutoDeploy(false);  96  configureEngine(tomcat.getEngine());  97         for (Connector additionalConnector : this.additionalTomcatConnectors) {  98  tomcat.getService().addConnector(additionalConnector);  99  } 100         // 准備上下文
101  prepareContext(tomcat.getHost(), initializers); 102         // 返回TomcatWebServer服務
103         return getTomcatWebServer(tomcat); 104  } 105 
106     /**
107  * Create a new {@link TomcatWebServer} instance. 108  * @param tomcat the underlying Tomcat server 109  * @param autoStart if the server should be started 110  * @param shutdown type of shutdown supported by the server 111  * @since 2.3.0 112      */
113     public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) { 114         Assert.notNull(tomcat, "Tomcat Server must not be null"); 115         this.tomcat = tomcat; 116         this.autoStart = autoStart; 117         this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null; 118         // 初始化Tomcat
119  initialize(); 120  } 121 
122     /**
123  * 初始化Tomcat 124  * @throws WebServerException 125      */
126     private void initialize() throws WebServerException { 127         logger.info("Tomcat initialized with port(s): " + getPortsDescription(false)); 128         synchronized (this.monitor) { 129             try { 130  addInstanceIdToEngineName(); 131 
132                 Context context = findContext(); 133                 context.addLifecycleListener((event) -> { 134                     if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) { 135                         // Remove service connectors so that protocol binding doesn't 136                         // happen when the service is started.
137  removeServiceConnectors(); 138  } 139  }); 140 
141                 // Start the server to trigger initialization listeners
142                 this.tomcat.start(); 143 
144                 // We can re-throw failure exception directly in the main thread
145  rethrowDeferredStartupExceptions(); 146 
147                 try { 148  ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader()); 149  } 150                 catch (NamingException ex) { 151                     // Naming is not enabled. Continue
152  } 153 
154                 // Unlike Jetty, all Tomcat threads are daemon threads. We create a 155                 // blocking non-daemon to stop immediate shutdown
156  startDaemonAwaitThread(); 157  } 158             catch (Exception ex) { 159  stopSilently(); 160  destroySilently(); 161                 throw new WebServerException("Unable to start embedded Tomcat", ex); 162  } 163  } 164     }
View Code

2.8、afterRefresh

afterReftesh() 刷新后處理,是個一空實現的擴展接口,留着后期擴展如用戶自定義容器刷新后的處理邏輯。

2.9、停止計時並打印啟動完畢相關日志

2.10、started

started 發布監聽應用啟動事件。

void started(ConfigurableApplicationContext context)  

 1     /**
 2  * 發布應用監聽啟動事件  3  * @param context  4      */
 5     void started(ConfigurableApplicationContext context) {  6         // listener.started(context) 中交由context.publishEvent()方法處理  7         // 實際上是發送了一個ApplicationStartedEvent的事件
 8         doWithListeners("spring.boot.application.started", (listener) -> listener.started(context));  9  } 10 
11     /**
12  * 發布應用啟動事件ApplicationStartedEvent. 13  * @param context 14      */
15  @Override 16     public void started(ConfigurableApplicationContext context) { 17         context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context)); 18  AvailabilityChangeEvent.publish(context, LivenessState.CORRECT); 19     }
View Code

2.11、callRunners

callRunners,執行runner主要是遍歷所有的runner獲取所有的ApplicationRuner 和CommandLineRunner 來初始化參數,其中callRuner(是一個回調函數)。

private void callRunners(ApplicationContext context, ApplicationArguments args) 

 1     /**
 2  * 執行runner 初始化參數  3  * @param context  4  * @param args  5      */
 6     private void callRunners(ApplicationContext context, ApplicationArguments args) {  7         List<Object> runners = new ArrayList<>();  8         runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());  9         runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); 10  AnnotationAwareOrderComparator.sort(runners); 11         // 遍歷所有runner
12         for (Object runner : new LinkedHashSet<>(runners)) { 13             if (runner instanceof ApplicationRunner) { 14                 /**
15  * 回調函數callRunner 處理 ApplicationRunner 16                  */
17  callRunner((ApplicationRunner) runner, args); 18  } 19             if (runner instanceof CommandLineRunner) { 20                 /**
21  * 回調函數callRunner 處理 CommandLineRunner 22                  */
23  callRunner((CommandLineRunner) runner, args); 24  } 25  } 26     }
View Code

2.12、running

running 發布上下文完成准備事件,listeners.running() 發布上下文完成准備事件同前面的listeners.started() 方法一樣,都是發布了一個running事件,代碼也相同。

void running(ConfigurableApplicationContext context) 

 1     /**
 2  * 發布上下文完成准備事件  3  * 與上面的 listeners.started() 方法一樣  4  * @param context  5      */
 6     void running(ConfigurableApplicationContext context) {  7         // listener.started(context) 中交由context.publishEvent()方法處理  8         // 實際上是發送了一個ApplicationStartedEvent的事件
 9         doWithListeners("spring.boot.application.running", (listener) -> listener.running(context)); 10  } 11 
12     /**
13  * 發布上下文完成准備事件 14  * Called immediately before the run method finishes, when the application context has 15  * been refreshed and all {@link CommandLineRunner CommandLineRunners} and 16  * {@link ApplicationRunner ApplicationRunners} have been called. 17  * @param context the application context. 18  * @since 2.0.0 19      */
20  @Override 21     public void running(ConfigurableApplicationContext context) { 22         context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context)); 23  AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC); 24     }
View Code

這也是SpringBoot啟動流程兩大過程中的第二階段的啟動方法run中最后一個方法了,該方法執行完成后,SpringApplication的run(String... args)方法執行結束,至此Spring Boot的ApplicationContext 啟動結束。

四、總結

SpringBoot啟動流程總結就是下面兩張圖片,一個創建SpringApplication實例,一個執行run方法,所有的貓膩都在其中。

 

 

 

 

 

君生我未生

    君生我已老

        君恨我生遲

                我恨君生早

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM