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方法,所有的猫腻都在其中。

 

 

 

 

 

君生我未生

    君生我已老

        君恨我生迟

                我恨君生早

 

 

 

 

 

posted @ 2022-04-04 15:30  涛姐涛哥  阅读(11477)  评论(1编辑  收藏  举报