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

     

     

     

     

     

    君生我未生

        君生我已老

            君恨我生迟

                    我恨君生早

     

     

     

     

     

  • 相关阅读:
    C#基础入门教程-简介和环境
    学习总结 | 10 万引大佬分享「做科研、写论文、发论文 10 大技巧」
    openGauss每日一练第9天 | openGauss中一个表空间可以存储多个数据库
    [vue3] 统一页面导航标签
    Go语言圣经的知识图谱
    高度“智慧”护航先进技术,王力重新定义“智慧门”
    Proteus 8 Professional跑Keil uVision4的文件
    PLC-Recorder以2ms的高速采集西门子S7-1500数据的方法
    MySQL第六讲·where和having的异同?
    Tomcat打包部署spring boot项目
  • 原文地址:https://www.cnblogs.com/taojietaoge/p/16075820.html