SpringBoot源码系列:
一文搞懂Spring Boot中java -jar启动jar包的原理
一文搞懂SpringBoot启动流程及自动配置
一文搞懂SpringBoot内嵌的Tomcat
一文搞懂SpringApplication对象的构建及spring.factories的加载时机
一文搞懂Spring Boot 事件监听机制
1、启动SpringBoot启动类SpringbootdemoApplication中的main方法。
@SpringBootApplication
public class SpringbootdemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootdemoApplication.class, args);
}
}
2、调用SpringApplication.run(SpringbootdemoApplication.class, args),该方法是一个静态方法。
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
3、继续调用SpringApplication内部的run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
,且构建了一个SpringApplication对象,应用程序将从指定的主要来源加载Bean
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//resourceLoader赋值为Null
this.resourceLoader = resourceLoader;
//primarySources不为空,继续向下执行。为空抛异常
Assert.notNull(primarySources, "PrimarySources must not be null");
//将SpringbootdemoApplication(启动类)赋值给primarySources
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//从classpath类路径推断Web应用类型,有三种Web应用类型,分别是
//NONE: 该应用程序不应作为 Web 应用程序运行,也不应启动嵌入式 Web 服务器
//SERVLET: 该应用程序应作为基于 servlet 的 Web 应用程序运行,并应启动嵌入式 servlet Web 服务器。
//REACTIVE: 该应用程序应作为响应式 Web 应用程序运行,并应启动嵌入式响应式 Web 服务器
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//初始化bootstrapRegistryInitializers,通过getSpringFactoriesInstances()获取工厂实例,
//底层使用的是反射Class> instanceClass = ClassUtils.forName(name, classLoader)动态加载实例对象。
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
//初始化ApplicationContextInitializer集合
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//初始化ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//获取StackTraceElement数组遍历,通过反射获取堆栈中有main方法的类。
this.mainApplicationClass = deduceMainApplicationClass();
}
4、运行 Spring 应用程序,创建并刷新一个新的 ApplicationContext。
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
//通过BootstrapRegistryInitializer来initialize默认的DefaultBootstrapContext
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
//配置java.awt.headless属性
configureHeadlessProperty();
//获取SpringApplicationRunListeners监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
//启动SpringApplicationRunListeners监听,表示SpringApplication启动(触发ApplicationStartingEvent事件)
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
//创建ApplicationArguments对象,封装了args参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//做相关环境准备,绑定到SpringApplication,返回可配置环境对象ConfigurableEnvironment
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
//配置spring.beaninfo.ignore,设置为true.即跳过搜索Bean信息
configureIgnoreBeanInfo(environment);
//控制台打印SpringBoot的Banner(横幅)标志
Banner printedBanner = printBanner(environment);
//根据WebApplicationType从ApplicationContextFactory工厂创建ConfigurableApplicationContext
context = createApplicationContext();
//设置ConfigurableApplicationContext中的ApplicationStartup为DefaultApplicationStartup
context.setApplicationStartup(this.applicationStartup);
//应用所有的ApplicationContextInitializer容器初始化器初始化context,触发ApplicationContextInitializedEvent事件监听,打印启动日志信息,启动Profile日志信息。
//ConfigurableListableBeanFactory中注册单例Bean(springApplicationArguments),并为该BeanFactory中的部分属性赋值。
//加载所有的source.并将Bean加载到ConfigurableApplicationContext,触发ApplicationPreparedEvent事件监听
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//刷新容器(在方法中集成了Web容器具体请看 https://editor.csdn.net/md/?articleId=123136262)
refreshContext(context);
//刷新容器的后置处理(空方法)
afterRefresh(context, applicationArguments);
//启动花费的时间
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
//打印日志Started xxx in xxx seconds (JVM running for xxxx)
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
//触发ApplicationStartedEvent事件监听。上下文已刷新,应用程序已启动。
listeners.started(context, timeTakenToStartup);
//调用ApplicationRunner和CommandLineRunner
callRunners(context, applicationArguments);
}
//处理运行时发生的异常,触发ApplicationFailedEvent事件监听
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
//启动准备消耗的时间
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
//在run方法完成前立即触发ApplicationReadyEvent事件监听,表示应用上下文已刷新,并且CommandLineRunners和ApplicationRunners已被调用。
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
SpringBoot的启动类上总是有@SpringBootApplication这个注解。接下来我们来了解一下这个注解。进入@SpringBootApplication源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
......
}
由此可见,@SpringBootApplication注解是一个组合注解,由@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan注解组成。
@SpringBootConfiguration其实就是一个@Configuration,表明这是一个配置类,可以向容器注入组件。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
......
}
@EnableAutoConfiguration由@AutoConfigurationPackage和@Import({AutoConfigurationImportSelector.class})注解组成。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
......
}
@AutoConfigurationPackage内部用到了@Import导入Registrar
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
......
}
Registrar实现了ImportBeanDefinitionRegistrar接口,因此可将组件都扫描注冊到 spring 容器中
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
}
}
@AutoConfigurationPackage将主配置类(@SpringBootApplication
标注的类)所在包下的所有组件都扫描注册到Spring容器中。
@Import({AutoConfigurationImportSelector.class}) 将AutoConfigurationImportSelector(自动配置导入选择器)导入容器中
AutoConfigurationImportSelector类中的selectImports()方法的作用是选择导入过滤后的自动配置
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
getAutoConfigurationEntry(AnnotationMetadata annotationMetadata)根据annotationMetadata(即我们的启动类SpringbootdemoApplication)获取AutoConfigurationEntry
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
//获取注解属性
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//从META-INF/spring.factories中获取候选配置。
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
//去除重复配置
configurations = this.removeDuplicates(configurations);
//获取注解中的排除项
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
//检查排除类
this.checkExcludedClasses(configurations, exclusions);
//从上面的候选配置中移除所有排除的配置类
configurations.removeAll(exclusions);
//通过ConfigurationClassFilter筛选配置
configurations = this.getConfigurationClassFilter().filter(configurations);
//触发自动配置导入事件
this.fireAutoConfigurationImportEvents(configurations, exclusions);
//返回排除后的自动配置Entry
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
总结SpringBoot启动原理
1、调用有@SpringBootApplication注解的启动类的main方法
2、通过调用SpringApplication内部的run()方法构建SpringApplication对象。
创建SpringApplication对象:
2.1 PrimarySources 不为空,将启动类赋值给primarySources 对象。
2.2 从classpath类路径推断Web应用类型,有三种Web应用类型NONE、SERVLET、REACTIVE
2.3 初始化bootstrapRegistryInitializers
2.4 初始化ApplicationContextInitializer集合
2.5 初始化ApplicationListener
2.6 获取StackTraceElement数组遍历,通过反射获取堆栈中有main方法A的。
3、调用SpringBootApplication的run方法。
4、long startTime = System.nanoTime(); 记录项目启动时间。
5、通过BootstrapRegistryInitializer来初始化DefaultBootstrapContext
6、getRunListeners(args)获取SpringApplicationRunListeners监听器
7、 listeners.starting()触发ApplicationStartingEvent事件
8、prepareEnvironment(listeners, bootstrapContext, applicationArguments) 将配置文件读取到容器中,返回ConfigurableEnvironment 对象。
9、printBanner(environment) 打印Banner图,即SpringBoot启动时的图案。
10、根据WebApplicationType从ApplicationContextFactory工厂创建ConfigurableApplicationContext,并设置ConfigurableApplicationContext中的ApplicationStartup为DefaultApplicationStartup
11、 调用prepareContext()初始化context等,打印启动日志信息,启动Profile日志信息,并为BeanFactory中的部分属性赋值。
12、刷新容器,在该方法中集成了Tomcat容器
13、加载SpringMVC.
14、刷新后的方法,空方法,给用户自定义重写afterRefresh()
15、Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime)算出启动花费的时间。
16、打印日志Started xxx in xxx seconds (JVM running for xxxx)
17、listeners.started(context, timeTakenToStartup)触发ApplicationStartedEvent事件监听。上下文已刷新,应用程序已启动。
18、调用ApplicationRunner和CommandLineRunner
19、返回上下文。