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

1 private List getBootstrapRegistryInitializersFromSpringFactories(){
 2         ArrayList 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      * 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  Collection getSpringFactoriesInstances(Class 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 names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
 21         // 通过反射创建这些类
 22         List 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      * 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 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> loadSpringFactories(ClassLoader classLoader) {
 60         // 获取相应类加载器中内容
 61         Map> 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 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  List createSpringFactoriesInstances(Class type, Class[] parameterTypes,
114                                                        ClassLoader classLoader, Object[] args, Set names) {
115         List 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 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      *
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 )

Spring Boot启动流程
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      * 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 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                  *
 70                  *             org.springframework.boot
 71                  *             spring-boot-starter-tomcat
 72                  *
 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
 2     org.springframework.boot
 3     spring-boot-starter-web
 4
 5
 6             org.springframework.boot
 7             spring-boot-starter-tomcat
 8
 9
10
11
12     org.springframework.boot
13     spring-boot-starter-jetty
14

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

君生我未生

君生我已老

君恨我生迟

我恨君生早

Original: https://www.cnblogs.com/taojietaoge/p/16075820.html
Author: 涛姐涛哥
Title: Spring Boot启动流程

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/612283/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • MySQL索引详解,面试必问

    1、什么是索引? 索引是帮助MySQL高效获取数据的数据结构(有序)。 除了数据之外,数据库系统还维护满足特定查找算法的数据结构,这些查找算法以某种方式引用(指向)数据,从而可以在…

    数据库 2023年5月24日
    0108
  • MySQL日志系统bin log、redo log和undo log

    MySQL日志系统bin log、redo log和undo log 今人不见古时月,今月曾经照古人。 简介:日志是MySQL数据库的重要组成部分,记录着数据库运行期间各种状态信息…

    数据库 2023年6月14日
    076
  • 根据温度、气压计算海拔高度

    基本概念 标准大气压:表示气压的单位,习惯上常用水银柱高度。例如,一个标准大气压等于760毫米高的水银柱的重量,它相当于一平方厘米面积上承受1.0336公斤重的大气压力。由于各国所…

    数据库 2023年6月14日
    0120
  • Redis缓存相关的几个问题

    1 缓存穿透 缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,进而给数据库带来…

    数据库 2023年6月14日
    073
  • HttpServletRequest 类

    HttpServletRequest类有什么作用 HttpServletRequest 类的常用方法 如何获取请求参数 doGet 请求的中文乱码解决: POST 请求的中文乱码解…

    数据库 2023年6月11日
    074
  • 数据中有emoji,导致插入不了数据库

    前言 前两天负责的系统,因为需要获取用户的昵称并进行入库,但是有个别用户的昵称中存在emoji表情,导致入库时报错。 报错内容: java.sql.SQLException: In…

    数据库 2023年5月24日
    0113
  • 一次较波折的MySQL调优

    春节假期的一天,阳光明媚,春暖花开,恰逢冬奥会开幕,想着这天一定是生肖吉日,就能顺风顺水了。没想到,我遇到了一位客户,有点波折。 [En] Spring Festival holi…

    数据库 2023年5月24日
    070
  • 《SpringBoot官网文档:2.1.5》

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    数据库 2023年6月14日
    076
  • 牛客SQL刷题第三趴——SQL必知必会

    【问题】编写 SQL 语句,从 Products 表中检索产品名称(prod_name)和描述(prod_desc),仅返回在描述中以先后顺序同时出现 toy 和 carrots …

    数据库 2023年5月24日
    071
  • logrotate command in Linux

    背景 在生产过程中,由于磁盘空间、保留周期等因素,会对系统、应用等日志提出要求,要求系统日志定期进行轮转、压缩和删除,从而减少开销,而系统自带的 logrotate 则是一个简单又…

    数据库 2023年6月14日
    0159
  • 19-TCP、UDP的区别和应用场景

    可靠性TCP 提供交付保证,这意味着一个使用TCP协议发送的消息是保证交付给客户端的,如果消息在传输过程中丢失,那么它将重发。UDP是不可靠的,它不提供任何交付的保证,一个数据包在…

    数据库 2023年6月16日
    091
  • mysqldump 在 StoneDB 中的使用注意事项

    此场景是利用mysqldump从InnoDB导出,然后再导入StoneDB,在导入StoneDB前,需要对导出文件做如下修改。1)修改存储引擎 CREATE TABLE t_use…

    数据库 2023年5月24日
    092
  • 数据库读写分离

    ———-数据库读写分离———- 环境准备:(两台虚拟机(centos7)可以连接外网 步骤1: 安装数据库,…

    数据库 2023年6月16日
    0103
  • 主从复制直接转换MGR_5.7验证试验

    IP port role info 192.168.188.51 4000 node1 master 192.168.188.52 4000 node2 slave1 192.16…

    数据库 2023年6月16日
    0213
  • Linux巡检脚本

    #!/bin/bash sys:centos6.x/7.x [ $(id -u) -ne 0 ] && echo "&#x8BF7;&#x…

    数据库 2023年6月14日
    0103
  • 获取不到数据库连接问题

    org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; …

    数据库 2023年6月11日
    077
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球