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/REACTIVE21 this.webApplicationType = WebApplicationType.deduceFromClasspath();22 // 从 Spring 工厂获取 Bootstrap Registry Initializers23 this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();24 // 获取 Spring 工厂实例 -> 容器上下文相关的初始化25 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));26 // 获取 Spring 工厂实例 -> 设置应用程序监听器27 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));28 // 推导出主应用程序类,即从当前的栈信息中寻找main所在主类:com.iot.SpringBootLoveApplication29 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集合,再存入 initializers12 * 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.SpringBootLoveApplication11 if ("main".equals(stackTraceElement.getMethodName())) {12 return Class.forName(stackTraceElement.getClassName());13 }14 }15 }16 catch (ClassNotFoundException ex) {17 // Swallow and continue18 }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=900031 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);32 //33 /**34 * 准备环境 prepareEnvironment 是个硬茬,里面主要涉及到35 * getOrCreateEnvironment、configureEnvironment、configurePropertySources、configureProfiles36 * environmentPrepared、bindToSpringApplication、attach诸多方法可以在下面的例子中查看37 */38 ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);39 // 配置忽略的 bean40 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启动内嵌的 Tomcat52 */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 // 初始化 defaultProperties104 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 * 配置Profiles130 *131 * @param environment132 * @param args133 */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 initialized143 // 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 bootstrapContext154 * @param environment155 */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 the164 * {@link ApplicationContext} has been created.165 *166 * @param environment the environment167 * @deprecated since 2.4.0 for removal in 2.6.0 in favor of168 * {@link #environmentPrepared(ConfigurableBootstrapContext, ConfigurableEnvironment)}169 */170 @Deprecated171 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 bind183 */184 protected void bindToSpringApplication(ConfigurableEnvironment environment) {185 try {186 // 将environment绑定到SpringApplication187 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 specified196 * {@link Environment}. Adapts each {@link PropertySource} managed by the environment197 * to a {@link ConfigurationPropertySource} and allows classic198 * {@link PropertySourcesPropertyResolver} calls to resolve using199 * {@link ConfigurationPropertyName configuration property names}.200 * <p>201 * The attached resolver will dynamically track any additions or removals from the202 * underlying {@link Environment} property sources.203 *204 * @param environment the source environment (must be an instance of205 * {@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获取PropertySources212 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 * @return10 */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.resourceLoader17 : 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 environment32 * @param sourceClass33 * @param logger34 * @return35 */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 environment51 * @return52 */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 environment75 * @return76 */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 // Ignore91 }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 应用环境对应 AnnotationConfigServletWebServerApplicationContext11 case SERVLET:12 return new AnnotationConfigServletWebServerApplicationContext();13 // 响应式编程对应 AnnotationConfigReactiveWebServerApplicationContext14 case REACTIVE:15 return new AnnotationConfigReactiveWebServerApplicationContext();16 // 默认为 Spring 环境 AnnotationConfigApplicationContext17 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 metrics31 * during startup.32 * @param applicationStartup the new context event factory33 * @since 5.334 */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 beans37 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();38 // 注册添加特定的单例bean39 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 sources51 // 加载所有资源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 we100 // 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 server109 * @param autoStart if the server should be started110 * @param shutdown type of shutdown supported by the server111 * @since 2.3.0112 */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 // 初始化Tomcat119 initialize();120 }121 122 /**123 * 初始化Tomcat124 * @throws WebServerException125 */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't136 // happen when the service is started.137 removeServiceConnectors();138 }139 });140 141 // Start the server to trigger initialization listeners142 this.tomcat.start();143 144 // We can re-throw failure exception directly in the main thread145 rethrowDeferredStartupExceptions();146 147 try {148 ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());149 }150 catch (NamingException ex) {151 // Naming is not enabled. Continue152 }153 154 // Unlike Jetty, all Tomcat threads are daemon threads. We create a155 // blocking non-daemon to stop immediate shutdown156 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 context14 */15 @Override16 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 // 遍历所有runner12 for (Object runner : new LinkedHashSet<>(runners)) {13 if (runner instanceof ApplicationRunner) {14 /**15 * 回调函数callRunner 处理 ApplicationRunner16 */17 callRunner((ApplicationRunner) runner, args);18 }19 if (runner instanceof CommandLineRunner) {20 /**21 * 回调函数callRunner 处理 CommandLineRunner22 */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 has15 * been refreshed and all {@link CommandLineRunner CommandLineRunners} and16 * {@link ApplicationRunner ApplicationRunners} have been called.17 * @param context the application context.18 * @since 2.0.019 */20 @Override21 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方法,所有的猫腻都在其中。
君生我未生
君生我已老
君恨我生迟
我恨君生早