Spring Boot简介
Spring Boot 是什么
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。
spring大家都知道,boot是启动的意思。所以,spring boot其实就是一个启动spring项目的一个工具而已。从最根本上来讲,Spring Boot就是一些库的集合,它能够被任意项目的构建系统所使用。
Spring Boot可以做什么,它解决了哪些问题
SpringBoot是伴随着Spring4.0诞生的,从字面理解,Boot是引导的意思,因此SpringBoot帮助开发者快速搭建Spring框架。
SpringBoot帮助开发者快速启动一个Web容器。
SpringBoot 继承了原有 Spring 框架的优秀基因。
SpringBoot 简化了使用Spring的过程。
Spring由于其繁琐的配置,一度被人认为“配置地狱”,各种XML、Annotation配置,让人眼花缭乱,而且如果出错了也很难找出原因。
Spring Boot更多的是采用Java Config的方式,对Spring进行配置。
Spring Boot启动原理
Spring Boot 启动类
@SpringBootApplication
2 public class Application {
3 public static void main(String[] args) {
4 SpringApplication.run(Application.class, args);
5 }
6 }
@SpringBootApplication 是 Sprnig Boot 项目的核心注解,目的是开启自动配置,所以要揭开 Spring Boot 的神秘面纱,我们需要从这里开始。
SpringBootApplication背后的秘密
@SpringBootApplication 注解是 Spring Boot 的核心注解,它是一个组合注解:
@Target({ElementType.TYPE})//注解的适用范围,其中TYPE用于描述类、接口(包括包注解类型)或enum声明
@Retention(RetentionPolicy.RUNTIME)//注解的生命周期,保留到class文件中(三个生命周期)
@Documented//表明这个注解应该被javadoc记录
@Inherited//子类可以继承该注解
@SpringBootConfiguration//继承了Configuration,表示当前是注解类
@EnableAutoConfiguration//开启 Spring Boot的注解功能,Spring Boot 的四大神器之一,其借助 @import 的帮助
@ComponentScan(//扫描路径设置(具体使用待确认)
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
。。。。省略
}
进入 @SpringBootApplication 注解后我们重点关注最后三个注解:
1)@SpringBootConfiguration // 继承了Configuration,表示当前是注解类
2)@EnableAutoConfiguration // 开启 Spring Boot 的注解功能,Spring Boot 的四大神器之一,其借助 @import 的帮助
3)@ComponentScan(excludeFilters = { // 扫描路径设置
即 @SpringBootApplication = (默认属性)@Configuration + @EnableAutoConfiguration + @ComponentScan。
接下来分别介绍这 3 个 Annotation。,增加对 SpringbootApplication 的理解。
@SpringBootConfiguration
@SpringBootConfiguration 注解表示 Spring Boot 配置类。查看 @SpringBootConfiguration 注解源码,核心代码具体如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
从源码可以看出 @SpringBootConfiguration 注解内部有一个核心注解 @Configuration,该注解是 Spring 框架提供的,表示当前类为一个配置类(XML配置文件的注解表现形式),并可以被组件扫描器扫描。由此可见,@SpringBootConfiguration 注解的作用与 @Configuration 注解相同,都是标识一个可以被组件扫描器扫描的配置类,只不过 @SpringBootConfiguration 是被 Spring Boot 进行了重新封装命名而已 。
@EnableAutoConfiguration
帮助 Spring Boot 应用将所有符合条件的 @Configuration 配置都加载到当前 Spring Boot,并创建对应配置类的Bean,并把该 Bean 实体交给 IoC 容器进行管理。
@EnableAutoConfiguration的实现代码如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
可以看到 AutoConfigurationPackage注解 他的作用就是将添加该注解的类所在的package 作为 自动配置package 进行管理,也就是说当SpringBoot应用启动时默认会将启动类所在的package作为自动配置的package。
其中最关键的是 @Import(AutoConfigurationImportSelector.class),借助 AutoConfigurationImportSelector,@EnableAutoConfiguration 可以帮助 Spring Boot 应用将所有符合条件的 @Configuration 配置都加载到当前 Spring Boot 创建并使用的IoC容器。
这里 AutoConfigurationImportSelector 直接实现了 DeferredImportSelector,DeferredImportSelector继承了ImportSelector 接口。
public interface ImportSelector {
String[] selectImports(AnnotationMetadata var1);
}
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
configurations = sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
追踪主要看 getCandidateConfigurations 方法,里面的调用逻辑在 loadFactoryNames, 加载了 “META-INF/spring.factories”; 里面的数据这里获取的是配置的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration里面的值。
spring.factories文件是一组key=value的集合,如下图:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
....
以上是从 Spring Boot 的 autoconfigure 依赖包中的 META-INF/spring.factories 配置文件中获取的一段内容,可以很好地说明问题。归根结底,@EnableAutoConfiguration 自动配置的原理就是:从 classpath 中搜寻所有的 META-INF/spring.factories 配置文件,并将其中的 org.spring-framework.boot.autoconfigure.EnableAutoConfiguration 对应的配置项通过反射实例化为对应的标注了 @Configuration 的 javaConfig 形式的 IOC 容器配置类,然后汇总为一整个并加载到 IOC 容器。
@ComponentScan
@ComponentScan 这个注解在 Spring 中很重要,它对应 XML 配置中的元素,@ComponentScan 的功能其实就是自动扫描并加载符合条件的组件(比如 @Component 和 @Repository 等)或者 bean 定义,最终将这些 bean 定义加载到 IoC 容器中。
我们可以通过 basePackages 等属性来细粒度的定制 @ComponentScan 自动扫描的范围,如果不指定,则默认Spring 框架实现会从声明 @ComponentScan 所在类的 package 进行扫描。
@ComponentScan 告诉 Spring 哪个 packages 的用注解标识的类 会被 Spring 自动扫描并且装入bean容器。
如果你有个类用 @Controller 注解标识了,那么,如果不加上 @ComponentScan,自动扫描该 Controller ,那么该Controller 就不会被 Spring 扫描到,更不会装入 Spring 容器中,因此你配置的这个 Controller 也没有意义。
参数的作用:
basePackageClasses:对basepackages()指定扫描注释组件包类型安全的替代。
excludeFilters:指定不适合组件扫描的类型。
includeFilters:指定哪些类型有资格用于组件扫描。
lazyInit:指定是否应注册扫描的beans为lazy初始化。
nameGenerator:用于在Spring容器中的检测到的组件命名。
resourcePattern:控制可用于组件检测的类文件。
scopedProxy:指出代理是否应该对检测元件产生,在使用过程中会在代理风格时尚的范围是必要的。
scopeResolver:用于解决检测到的组件的范围。
useDefaultFilters:指示是否自动检测类的注释
SpringApplication执行流程
@SpringBootApplication
public class QuartJobApplication {
public static void main(String[] args) {
SpringApplication.run(QuartJobApplication.class, args);
}
}
进入SpringApplication.run(App.class, args)代码段的 run 方法:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
// 调用静态run方法
return run(new Class[]{primarySource}, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 创建SpringApplication实例,执行实例run方法
return (new SpringApplication(primarySources)).run(args);
}
SpringApplication 构造方法
点击进入 SpringApplication 构造方法:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//设置一些默认参数
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//保存主配置类到一个 Set 集合 primarySources 中
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
//判断应用类型,后面会根据类型初始化对应的环境,常用的一般都是 servlet 环境
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//初始化classpath下 META-INF/spring.factories中已配置的ApplicationContextInitializer
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//初始化classpath下所有已配置的 ApplicationListener
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//根据调用栈,判断出 main 方法的类名
this.mainApplicationClass = this.deduceMainApplicationClass();
//执行完毕,SpringApplication对象就创建出来了,返回调用SpringApplication对象的run方法
}
SpringApplication 构造方法中主要做了以下几件事情:
- 初始化各种参数赋默认值
- 配置primarySources
- 配置环境
- 从META-INF中的spring.factories中获取启动加载器、初始化器、监听器
- 配置启动类
上面几点跳转方法源码:
WebApplicationType.deduceFromClasspath():该方法通过判断指定路径下的文件是否存在并可以加载来判断当前项目类型,如 SERVLET、REACTIVE、NONE
blic enum WebApplicationType {
NONE,
SERVLET,
REACTIVE;
// 常量值
private static final String[] SERVLET_INDICATOR_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
private WebApplicationType() {
}
//判断 应用的类型
//NONE: 应用程序不是 web 应用,也不应该用 web 服务器去启动
//SERVLET: 应用程序应作为基 于servlet 的 web 应用程序运行,并应启动嵌入式 servlet web(tomcat)服务器。
//REACTIVE: 应用程序应作为 reactive web 应用程序运行,并应启动嵌入式 reactive web 服务器。
static WebApplicationType deduceFromClasspath() {
//classpath下必须存在org.springframework.web.reactive.DispatcherHandler
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
return REACTIVE;
} else {
String[] var0 = SERVLET_INDICATOR_CLASSES;
int var1 = var0.length;
for(int var2 = 0; var2 < var1; ++var2) {
String className = var0[var2];
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return NONE;
}
}
//classpath环境下存在javax.servlet.Servlet或者org.springframework.web.context.ConfigurableWebApplicationContext
return SERVLET;
}
}
static WebApplicationType deduceFromApplicationContext(Class<?> applicationContextClass) {
if (isAssignable("org.springframework.web.context.WebApplicationContext", applicationContextClass)) {
return SERVLET;
} else {
return isAssignable("org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext", applicationContextClass) ? REACTIVE : NONE;
}
}
private static boolean isAssignable(String target, Class<?> type) {
try {
return ClassUtils.resolveClassName(target, (ClassLoader)null).isAssignableFrom(type);
} catch (Throwable var3) {
return false;
}
}
}
返回类型是 WebApplicationType 的枚举类型, WebApplicationType 有三个枚举,三个枚举的解释如下:
- WebApplicationType.REACTIVE classpath 下存在 org.springframework.web.reactive.DispatcherHandler
- WebApplicationType.SERVLET classpath下存在 javax.servlet.Servlet 或者org.springframework.web.context.ConfigurableWebApplicationContext
- WebApplicationType.NONE 不满足以上条件
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class))
初始化 classpath 下 META-INF/spring.factories 中已配置的 ApplicationContextInitializer。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
//调用下面重载方法getSpringFactoriesInstances
return this.getSpringFactoriesInstances(type, new Class[0]);
}
//通过指定的classloader 从META-INF/spring.factories获取指定的Spring的工厂实例
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = this.getClassLoader();
//通过指定的classLoader从 META-INF/spring.factories 的资源文件中,
//读取 key 为 type.getName() 的 value
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//创建Spring工厂实例
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//对Spring工厂实例排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
在 getSpringFactoriesInstances 方法中 loadFactoryNames 它是spring-core中提供的从 META-INF/spring.factories 中获取指定的类(key)的同一入口方法,这里面获取的是 key 为 org.springframework.context.ApplicationContextInitializer 的类。
ApplicationContextInitializer 是Spring 框架的类, 这个类的主要目的就是在 ConfigurableApplicationContext 调用refresh() 方法之前,回调这个类的 initialize 方法。通过 ConfigurableApplicationContext 的实例获取容器的环境Environment,从而实现对配置文件的修改等工作。
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class))
执行 setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); 和 ApplicationContextInitializer 操作一样;
从类路径下找到 META-INF/spring.factories 配置的所有 ApplicationListener
this.mainApplicationClass = this.deduceMainApplicationClass()
从多个配置类中哪个类中有main()的主配置类
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
StackTraceElement[] var2 = stackTrace;
int var3 = stackTrace.length;
for(int var4 = 0; var4 < var3; ++var4) {
StackTraceElement stackTraceElement = var2[var4];
if ("main".equals(stackTraceElement.getMethodName())) { //判断是否包含 main 方法
return Class.forName(stackTraceElement.getClassName());
}
}
} catch (ClassNotFoundException var6) {
}
return null;
}
调用 SpringApplication 对象的 run 方法
下面我们直接进入 run 方法看源码:
/**
* 运行spring应用,并刷新一个新的 ApplicationContext(Spring的上下文)
* ConfigurableApplicationContext 是 ApplicationContext 接口的子接口。在 ApplicationContext
* 基础上增加了配置上下文的工具。 ConfigurableApplicationContext是容器的高级接口
*/
public ConfigurableApplicationContext run(String... args) {
//创建 StopWatch 对象,并启动 StopWatch,主要用于简单统计 run 启动过程的时长
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//初始化应用上下文和异常报告集合
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
//从类路径下META‐INF/spring.factories获取SpringApplicationRunListeners
//1、获取并启动监听器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//遍历上一步获取的所有 SpringApplicationRunListener,调用其 starting 方法
listeners.starting();
Collection exceptionReporters;
try {
// 创建 ApplicationArguments 对象 初始化默认应用参数类
// args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。如:--server.port=9000
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//2、准备环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
//处理需要忽略的Bean
this.configureIgnoreBeanInfo(environment);
//打印Banner,就是控制台显示那个 Spring 字符画
Banner printedBanner = this.printBanner(environment);
//3、创建 Spring 容器
context = this.createApplicationContext();
//获得异常报告器 SpringBootExceptionReporter 数组
//这一步的逻辑和实例化初始化器和监听器的一样,
//都是通过调用 getSpringFactoriesInstances 方法来获取配置的异常类名称并实例化所有的异常处理类。
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
//4、准备 Spring 容器
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//5、刷新容器
this.refreshContext(context);
//Spring 容器刷新后处理
this.afterRefresh(context, applicationArguments);
//停止 StopWatch 统计时长
stopWatch.stop();
//打印 Spring Boot 启动的时长日志。
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
//发布容器启动完成事件
listeners.started(context);
//执行实现了 ApplicationRunner 或者 CommandLineRunner 的 bean 的 run方法
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
//如果发生异常,则进行处理,并抛出 IllegalStateException 异常
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
//监听器发出正在运行中事件
listeners.running(context);
//返回最终的 Spring 容器对象
return context;
} catch (Throwable var9) {
//如果发生异常,则进行处理,并抛出 IllegalStateException 异常
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
根据源码分析,大致可以分为以下几个步骤:
步骤一:获取并启动监听器
获取 META-INF中的 spring.factories 文件中 key 为 org.springframework.boot.SpringApplicationRunListener的值。
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
//这里仍然利用了 getSpringFactoriesInstances 方法来获取实例,
//从 META-INF/spring.factories 中读取 Key 为org.springframework.boot.SpringApplicationRunListener 的 Values
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = this.getClassLoader();
//通过指定的 classLoader从 META-INF/spring.factories 的资源文件中,
//读取 key 为 type.getName() 的 value
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//创建 Spring 工厂实例
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//对 Spring 工厂实例排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
EventPublishingRunListener 监听器是 Spring 容器的启动监听器。
listeners.starting(); 开启了监听事件。
步骤二:准备环境
准备环境包括什么呢?包括计算机的环境,Java 环境,Spring 的运行环境,Spring 项目的配置(在SpringBoot中就是那个熟悉的application.properties/yml)等。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
//创建并配置相应的环境
ConfigurableEnvironment environment = getOrCreateEnvironment();
//根据用户配置,配置 environment系统环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 启动相应的监听器,其中一个重要的监听器 ConfigFileApplicationListener 就是加载项目配置文件的监听器。
listeners.environmentPrepared(environment);
//将环境绑定到SpringApplication
bindToSpringApplication(environment);
//如果非 web 环境,将环境转换成 StandardEnvironment
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
// 配置PropertySources对它自己的递归依赖
// 如果有 attach 到 environment 上的 MutablePropertySources ,则添加到 environment 的 PropertySource 中。
ConfigurationPropertySources.attach(environment);
return environment;
}
步骤三:创建Spring容器
在 Spring Boot 项目中,应用类型分为三种,如下代码所示:
public enum WebApplicationType {
/**
* 应用程序不是 web 应用,也不应该用 web 服务器去启动
*/
NONE,
/**
* 应用程序应作为基于 servlet 的 web 应用程序运行,并应启动嵌入式 servlet web(tomcat)服务器。
*/
SERVLET,
/**
* 应用程序应作为 reactive web 应用程序运行,并应启动嵌入式 reactive web 服务器。
*/
REACTIVE
}
对应三种应用类型,Spring Boot 项目有三种对应的应用上下文,根据 webApplicationType 进行判断,确定容器类型,如果该类型为 SERVLET,会通过反射装载对应的字节码,也就是,AnnotationConfigServletWebServerApplicationContext。
protected ConfigurableApplicationContext createApplicationContext() {
// 根据 webApplicationType 类型,获得 ApplicationContext 类型
// 这里创建容器的类型 还是根据 webApplicationType 进行判断的,
// 该类型为 SERVLET 类型,所以会通过反射装载对应的字节码,
// 也就是 AnnotationConfigServletWebServerApplicationContext
// 先判断有没有指定的实现类
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
} catch (ClassNotFoundException ex) {
throw new IllegalStateException("Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex);
}
}
// 创建 ApplicationContext 对象
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
这里我们来看一下 AnnotationConfigServletWebServerApplicationContext的继承关系:
从继承关系中我们可以看出,beanFactory 是在 AnnotationConfigServletWebServerApplicationContext 实现的接口GenericApplicationContext 中定义的。在上面createApplicationContext()方法中的, BeanUtils.instantiateClass(contextClass) 这个方法中,不但初始化了AnnotationConfigServletWebServerApplicationContext 类,也就是我们的上下文 context,同样也触发了GenericApplicationContext 类的构造函数,从而 IoC 容器也创建了。看他的构造函数我们发现了DefaultListableBeanFactory 类,是的,DefaultListableBeanFactory 就是 IoC 容器真实面目了。在后面的 refresh()方法分析中,DefaultListableBeanFactory 是无处不在的存在。
步骤四:准备Spring 容器
这一步主要实在容器刷新之前的准备工作,设置容器环境,包括各种变量参数等,其中包含一个非常关键的操作,将启动类注入容器,为后续开启自动化配置奠定基础
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//设置容器环境,包括各种变量
context.setEnvironment(environment);
//设置上下文的 bean 生成器和资源加载器
postProcessApplicationContext(context);
//执行容器中的ApplicationContextInitializer(包括 spring.factories和自定义的实例)
applyInitializers(context);
//触发所有 SpringApplicationRunListener 监听器的 contextPrepared 事件方法
listeners.contextPrepared(context);
//记录启动日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
//注册启动参数bean,这里将容器指定的参数封装成bean,注入容器
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
// 加载所有资源
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//加载我们的启动类,将启动类注入容器,为后续开启自动化配置奠定基础
load(context, sources.toArray(new Object[0]));
//触发所有 SpringApplicationRunListener 监听器的 contextLoaded 事件方法
listeners.contextLoaded(context);
//这块会对整个上下文进行一个预处理,比如触发监听器的响应事件、加载资源、设置上下文环境等等
}
步骤五:刷新容器
开启刷新 Spring 容器,通过 refresh 方法对整个 IOC 容器的初始化
private void refreshContext(ConfigurableApplicationContext context) {
//开启(刷新)Spring 容器,通过 refresh 方法对整个 IoC 容器的初始化(包括Bean资源的定位、解析、注册等等)
refresh(context);
// 注册 ShutdownHook 钩子
if (this.registerShutdownHook) {
try {
//向 JVM 运行时注册一个关机钩子,在 JVM 关机时关闭这个上下文,除非它当时已经关闭
context.registerShutdownHook();
} catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
从 run 方法的 refreshContext() 方法一路跟下去,最终来到 AbstractApplicationContext 类的 refresh() 方法。
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
//刷新上下文环境
this.prepareRefresh();
//这里是在子类中启动 refreshBeanFactory() 的地方
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
//准备bean工厂,以便在此上下文中使用
this.prepareBeanFactory(beanFactory);
try {
//设置 beanFactory 的后置处理
this.postProcessBeanFactory(beanFactory);
//调用 BeanFactory 的后处理器,这些处理器是在Bean 定义中向容器注册的
this.invokeBeanFactoryPostProcessors(beanFactory);
//注册Bean的后处理器,在Bean创建过程中调用
this.registerBeanPostProcessors(beanFactory);
//对上下文中的消息源进行初始化
this.initMessageSource();
//初始化上下文中的事件机制
this.initApplicationEventMulticaster();
//初始化其他特殊的Bean
this.onRefresh();
//检查监听Bean并且将这些监听Bean向容器注册
this.registerListeners();
//实例化所有的(non-lazy-init)单件
this.finishBeanFactoryInitialization(beanFactory);
//发布容器事件,结束Refresh过程
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
这里需要讲解的地方很多并且很深入,就不在一一讲了。
步骤六:Spring 容器刷新后处理
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
这里是一个空的方法 。
步骤七:发布容器启动完成事件
调用 SpringApplicationRunListener的 started 方法,当前 context 对象里这种类型的监听器只有EventPublishingRunListener,EventPublishingRunListener 会构造一个 ApplicationStartedEvent 事件并广播出去,执行 ConfigurableApplicationContext 的 publishEvent 方法也就是说在这里实在 spring 容器发布事件,并不是在 SpringApplication 中发布事件,和前面的 starting 是不同的,前面的 staring 是直接向 SpringApplication 中的监听器发布启动事件。
public void started(ConfigurableApplicationContext context) {
//执行所有SpringApplicationRunListener实现的started方法。
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
步骤八:执行Runners
用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序
Runner 运行器用于在服务启动时进行一些业务初始化操作,这些操作只在服务启动后执行一次。
Spring Boot提供了ApplicationRunner和CommandLineRunner两种服务接口
private void callRunners(ApplicationContext context, ApplicationArguments args) {
// 获得所有 Runner 们
List<Object> runners = new ArrayList<>();
// 获得所有 ApplicationRunner 实现类
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// 获得所有 CommandLineRunner 实现类
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// 排序 runners
AnnotationAwareOrderComparator.sort(runners);
// 遍历 Runner 数组,执行逻辑
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
步骤九:发布应用上下文就绪事件
表示在前面一切初始化启动都没有问题的情况下,使用运行监听器 SpringApplicationRunListener 持续运行配置好的应用上下文 ApplicationContext,这样整个 Spring Boot 项目就正式启动完成了。
public void running(ConfigurableApplicationContext context) {
//触发所有 SpringApplicationRunListener 监听器的 running 事件方法。
for (SpringApplicationRunListener listener : this.listeners) {
listener.running(context);
}
}
备注:这里需要注意的是,一些代码可能会因为版本关系导致代码不完全一样,但是处理逻辑基本是一样的