Spring Boot启动流程
- SpringApplication初始化
- SpringApplication.run()
- 开始服务
SpringApplication初始化
- 初始化流程:
- 配置resourceLoader
- 配置primarySources
- 配置webApplicationType
- 配置ApplicationContextInitialzer
- 配置ApplicationListener
- 配置mainApplicationClass
// Resource是Spring对物理资源的访问抽象
// ResourceLoader中只定义了一个用于获取Resource的getResource(String location)方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 设置在实例化SpringApplication时自定义的resourceLoader
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// primarySources 为 run 方法传入自定义的启动引导类(可以一次启动多个)
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推断web应用类
/*
根据 classpath下是否存在某个特征类来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
1. 如果仅存在Reactive的包,则为WebApplicationType.REACTIVE类型;
2. 如果Servlet和Reactive的包都不存在,则为WebApplicationType.NONE类型;
3. 其他情况都为WebApplicationType.SERVLET类型。
*/
this.webApplicationType = deduceWebApplicationType();
// 初始化 initializers 属性
/*
1. 通过SpringFactoriesLoader.loadFactoryNames(type, classLoader)方法,在 META-INF/spring.factories文件下查找ApplicationContextInitializer类型对应的资源名称。
2. 实例化上面的资源信息(初始化器)。
3. 对初始化器根据Ordered接口或者@Order注解进行排序
*/
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 初始化监听器,与初始化initializers属性类似
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推断应用引导类,调用栈中main方法所在的类作为引导类。
this.mainApplicationClass = deduceMainApplicationClass();
}
启动入口
- 包含两个核心组件:@SpringBootApplication注解和SpringApplication.run()静态方法。
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication
- 该注解是一个复合注解。它并没有定义新的属性,而是复用和组合其他已有注解,可以开启自动配置、自动扫描的功能。
- 包含三个核心注解:@Configuration、@ComponentScan、@EnableAutoConfiguration。
// Java中有四类元注解,@Target,@Retention,@Documented,@Inherited
// 注解的作用目标为接口、类、枚举
@Target(ElementType.TYPE)
// 注解的保留位置,会在class字节码文件中存在,在运行时可以通过反射获取到
@Retention(RetentionPolicy.RUNTIME)
// 该注解将被包含在javadoc中
@Documented
// 子类可以继承父类中的该注解
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM,
classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
/*
@AliasFor声明实现同一功能的不同属性,这些属性互为别名。
通过@AliasFor,即使用户使用的是@SpringBootApplication,
Spring还是可以通过AnnotationUtils#getAnnotation,
AnnotatedElementUtils#getMergedAnnotation等方法
解析到@SpringBootConfiguration,@EnableAutoConfiguration,
@ComponentScan等注解,并取得对应属性。
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
// 根据包路径扫描
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
// 直接根据class类扫描
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
@SpringBootConfiguration
- 该注解继承自@Configuration,作用也相同,都是用来声明当前类是一个配置类。会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到Spring容器中,且实例名就是方法名。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
@EnableAutoConfiguration
- 该注解实现自动化配置,借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器。
- 作用是从classpath中搜索所有META-INF/spring.factories配置文件,将其中org.springframework.boot.autoconfigure.EnableAutoConfiguration key对应的配置项加载到spring容器,只有spring.boot.enableautoconfiguration为true(默认为true)时,才启用自动配置。也可以根据class来排除(exclude),或根据class name(excludeName)来排除。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
// @Import通过快速导入的方式实现把实例加入spring的IOC容器中
// ImportSelector接口的方法的返回值都会被纳入到spring容器管理中
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
AutoConfigurationImportSelector.class
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// AnnotationMetadata可以获取当前类上注解的元数据,如注解名字,以及元注解信息等。从而判断是否开启了自动装配。
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
// 找到所有自动装配元数据属性文件,将所有配置信息加载到一个Properties对象中,AutoConfigurationMetadata可基于Properties实现。
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
// 获取注解的所有属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 利用SpringFactoriesLoader到classpath下的读取META-INF/spring.factories文件的配置,并返回一个字符串数组。
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);
}
}
@ComponentScan
- 该注解用于自动扫描被@Service/@Repository/@Component/@Controller注解标识的类,最终生成IoC容器的Bean。
- 可以通过设置basePackages,includeFilters,excludeFilters属性来动态确定自动扫描范围,类型以及不扫描的类型。默认情况扫描所有类型。
- 使用@SpringBootApplication注解,就使用了@ComponentScan的默认配置。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
//在一个类中可重复定义
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
//定义扫描的包
@AliasFor("basePackages")
String[] value() default {};
//定义扫描的包
@AliasFor("value")
String[] basePackages() default {};
//定义扫描的类
Class<?>[] basePackageClasses() default {};
//Bean name生成器
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
//作用域解析器
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
//作用域代理模式
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
//资源匹配模式
String resourcePattern() default "**/*.class";
//是否启动默认的过滤器
boolean useDefaultFilters() default true;
//当满足过滤条件时扫描
ComponentScan.Filter[] includeFilters() default {};
//当不满足过滤条件时扫描
ComponentScan.Filter[] excludeFilters() default {};
//是否延迟初始化
boolean lazyInit() default false;
//自定义过滤器
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Filter {
//过滤器类型,可以按注解类型或者正则式等过滤
FilterType type() default FilterType.ANNOTATION;
//定义过滤的类
@AliasFor("classes")
Class<?>[] value() default {};
//定义过滤的类
@AliasFor("value")
Class<?>[] classes() default {};
//匹配方式
String[] pattern() default {};
}
}
SpringApplication.run()
- 是SpringApplication的运行阶段,该阶段在初始化阶段完成的状态上,进一步完善运行时所需要准备的资源,随后启动Spring应用上下文,且在此期间伴随着SpringBoot和Spring事件的触发,形成SpringApplication完整的生命周期。
- 运行流程:
- 计时器开始计时
- Headless模式复制
- 发送ApplicationStartingEvent
- 配置环境模块
- 发送ApplicationEnvironmentPreparedEvent
- 打印banner
- 创建应用上下文对象
- 初始化失败分析器
- 关联SpringBoot组件与应用上下文对象
- 发送ApplicationContextInitializedEvent
- 加载sources到context
- 发送ApplicationPreparedEvent
- 刷新上下文
- 获取beanFactory
- 准备beanFactory
- 调用BeanFactoryPostProcessors
- 注册BeanPostProcessors
- 初始化MessageSource
- 注册listener beans
- 实例化单例bean
- 发布对应事件
- 清除缓存
- 注册钩子方法
- 计时器停止计时
- 发送ApplicationStartedEvent
- 调用框架启动扩展类
- 发送ApplicationReadyEvent
SpringApplication准备阶段
- 属于ApplicationContext启动前的阶段,范围从run()方法调用开始,到refreshContext()方法调用前。
public ConfigurableApplicationContext run(String... args) {
//记录程序运行时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//Spring 应用的上下文
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 设置headless模式,是在缺少显示设备、键盘或鼠标的系统配置。
configureHeadlessProperty();
// 通过getSpringFactoriesInstances获取 META-INF/spring.factories 文件下SpringApplicationRunListener对应的资源
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 创建 ApplicationArguments 对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 加载属性配置
/*
1. 创建ConfigurableEnvironment对象。
2. 配置environment变量。
3. 发布ApplicationEnvironmentPreparedEvent事件。
其对应的监听器为ConfigFileApplicationListener,当接收到上面的事件时,加载并实例化 “META-INF/spring.factories” 文件中EnvironmentPostProcessor类型的实现类,遍历并执行其postProcessEnvironment方法。
*/
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 处理需要忽略的Bean
configureIgnoreBeanInfo(environment);
// 打印 banner
Banner printedBanner = printBanner(environment);
// 根据webApplicationType的类型,创建不同的Spring应用上下文
context = createApplicationContext();
// 实例化 SpringBootExceptionReporter,用来报告关于启动过程中的错误
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 应用上下文的准备阶段
/*
1. 给context的属性做赋值,如设置环境变量,调用初始化器来初始化context。
2. 获取所有配置源信息,包括 Configuration Class、类名、包名及Spring XML 配置资源路径信息。
3. 加载 Spring 应用上下文配置源。将BeanDefinition加载到context中。
4. 发布上下文已准备事件ApplicationPreparedEvent。
*/
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 刷新应用上下文(自动装配,初始化 IOC 容器)
refreshContext(context);
...
}
}
Application启动阶段
- 本阶段的执行由refreshContext(ConfigurableApplicationContext)完成,其核心方法是AbstractApplicationContext#refresh。
- 随着该方法的执行,Spring Boot 核心特性也随之启动,如组件自动装配、嵌入式容器启动。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 准备刷新
prepareRefresh();
// 通知子类刷新内部bean工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 准备bean工厂以便在此上下文中使用
prepareBeanFactory(beanFactory);
try {
// 允许上下文子类中对bean工厂进行后处理
postProcessBeanFactory(beanFactory);
// 在bean创建之前调用BeanFactoryPostProcessors后置处理方法
invokeBeanFactoryPostProcessors(beanFactory);
// 注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 注册DelegatingMessageSource
initMessageSource();
// 注册multicaster
initApplicationEventMulticaster();
// 创建内置的Servlet容器
onRefresh();
// 注册Listener
registerListeners();
// 完成BeanFactory初始化,初始化剩余单例bean
finishBeanFactoryInitialization(beanFactory);
// 发布对应事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);
}
// 销毁已经创建的单例,以避免悬空资源。
destroyBeans();
// 重置'active'标志
cancelRefresh(ex);
// 将异常传播给调用者。
throw ex;
}
finally {
// 在Spring的核心中重置常见的自省缓存,因为我们可能不再需要单例bean的元数据了
resetCommonCaches();
}
}
}
ApplicationContext启动后阶段
public ConfigurableApplicationContext run(String... args) {
……
// 该方法方法并未给 Spring 应用上下文启动后阶段提供实现,而是将其交给开发人员自行扩展
afterRefresh(context, applicationArguments);、
// 停止计时器
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 发布 Spring 应用上下文已启动ApplicationStartedEvent事件
listeners.started(context);
// 调用实现了CommandLineRunner或者ApplicationRunner接口的类的 run 方法,得以满足需要在 Spring 应用上下文完全准备完毕后,执行一些操作的场景。
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
自动化装配
- 通过各种注解实现了类与类之间的依赖关系,容器在启动的时候Application.run,会调用EnableAutoConfigurationImportSelector.class的selectImports方法。
- selectImports方法最终会调用SpringFactoriesLoader.loadFactoryNames方法来获取一个全面的常用BeanConfiguration列表。
- loadFactoryNames方法会读取FACTORIES_RESOURCE_LOCATION(也就是spring-boot-autoconfigure.jar 下面的spring.factories),获取到所有的Spring相关的Bean的全限定名ClassName。
- selectImports方法继续调用filter(configurations, autoConfigurationMetadata);这个时候会根据这些BeanConfiguration里面的条件,来一一筛选,最关键的是
@ConditionalOnClass,这个条件注解会去classpath下查找,jar包里面是否有这个条件依赖类,所以必须有了相应的jar包,才有这些依赖类,才会生成IOC环境需要的一些默认配置Bean。
- 最后把符合条件的BeanConfiguration注入默认的EnableConfigurationPropertie类里面的属性值,并且注入到IoC环境当中。