当被问道springboot启动流程时,我依稀记得看过但是忘了,其实是没真正理解。
SpringBoot启动 分为两大部分
1、启动类上注解:@SpringBootApplication,构造SpringApplication
2、调用SpringApplication 对象的 run() 方法:
SpringApplication.run(Application.class, args)
一、新建SpringApplication对象
启动类中的main方法就一句
@SpringBootApplication
public class Application{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
但是重要的是 @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 {
...........
....
}
三个注解核心注解:@SpringBootConfiguration
,@ComponentScan
和@EnableAutoConfiguration
。
@SpringBootConfiguration
根据Javadoc可知,该注解作用就是将当前的类作为一个JavaConfig,然后触发注解@EnableAutoConfiguration和@ComponentScan的处理,本质上与@Configuration注解没有区别
@ComponentScan
扫描的 Spring 对应的组件,如 @Componet,@Repository。
我们可以通过 basePackages 等属性来细粒度的定制 @ComponentScan 自动扫描的范围,如果不指定,则默认Spring框架实现会从声明 @ComponentScan 所在类的package进行扫描,所以 SpringBoot 的启动类最好是放在根package下,我们自定义的类就放在对应的子package下,这样就可以不指定 basePackages
@EnableAutoConfiguration 重点
@EnableAutoConfiguration
点进去上面又有这两个注解比较重要 @AutoConfigurationPackage
、@Import(AutoConfigurationImportSelector.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
...
}
@AutoConfigurationPackage
一旦加上此注解,那么将会开启自动装配功能,简单点讲,Spring会试图在你的classpath下找到所有配置的Bean然后进行装配。当然装配Bean时,会根据若干个(Conditional)定制规则来进行初始化
① 注册当前启动类的根 package
② 注册 org.springframework.boot.autoconfigure.AutoConfigurationPackages 的 BeanDefinition。
@Import(AutoConfigurationImportSelector.class)
① 实现了 DeferredImportSelector 接口,该接口继承自 ImportSelector,根据 Javadoc 可知,多用于导入被 @Conditional 注解的Bean,之后会进行 filter 操作
② AutoConfigurationImportSelector中的内部类AutoConfigurationGroup
的process
方法,SpringBoot 启动时会调用该方法,进行自动装配的处理,见 - 自动装配
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if(!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
try {
AutoConfigurationMetadata ex = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 获取预先定义的应考虑的自动配置类名称
List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
configurations = this.sort(configurations, ex);
Set exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 通过filter过滤掉当前环境不需要自动装配的类,比如没有集成RabbitMQ,或者不满足@Conditional也不需要自动装配
configurations = this.filter(configurations, ex);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回需要自动装配的全路径类名
return (String[])configurations.toArray(new String[configurations.size()]);
} catch (IOException var6) {
throw new IllegalStateException(var6);
}
}
}
④ getCandidateConfigurations方法中 调用了 SpringFactoryiesLoader
类中的 loadFactoryNames
方法,它会读取META-INF/spring.factories下的EnableAutoConfiguration的配置,紧接着在进行排除与过滤,进而得到需要装配的类
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
⑤ 最后让所有配置在META-INF/spring.factories下的AutoConfigurationImportListener执行AutoConfigurationImportEvent事件
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
List listeners = this.getAutoConfigurationImportListeners();
if(!listeners.isEmpty()) {
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
Iterator var5 = listeners.iterator();
while(var5.hasNext()) {
AutoConfigurationImportListener listener = (AutoConfigurationImportListener)var5.next();
this.invokeAwareMethods(listener);
listener.onAutoConfigurationImportEvent(event);
}
}
}
1)自动装配还是利用了SpringFactoriesLoader来加载META-INF/spring.factoires文件里所有配置的EnableAutoConfgruation,它会经过exclude和filter等操作,最终确定要装配的类
2) 处理@Configuration的核心还是ConfigurationClassPostProcessor,这个类实现了BeanFactoryPostProcessor, 因此当AbstractApplicationContext执行refresh方法里的invokeBeanFactoryPostProcessors(beanFactory)方法时会执行自动装配
二、SpringApplication 执行 run 方法
实际执行的是 SpringApplication中同名的重载的run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
1、首先会实例化SpringApplication一个对象 2、在构造方法里初始化一些属性,比如webApplicationType,比如"SERVLET",初始化一些listeners
public SpringApplication(ResourceLoader resourceLoader, Object... sources) {
this.resourceLoader = resourceLoader;
initialize(sources);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
// 初始化webApplicationType,比如"SERVLET"
this.webEnvironment = deduceWebEnvironment();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 初始化一些listeners
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
最终执行的run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
//
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 最终会调用到AbstractApplicationContext#refresh方法,实际上就是Spring IOC容器的创建过程,并且会进行自动装配的操作
// 以及发布ApplicationContext已经refresh事件,标志着ApplicationContext初始化完成
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
// stopWatch停止计时,日志打印总共启动的时间
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
public ConfigurableApplicationContext run(String... args) {
//创建一个StopWatch实例,用来记录SpringBoot的启动时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
ArrayList exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
//通过SpringFactoriesLoader加载listeners:比如EventPublishingRunListener
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters1;
try {
DefaultApplicationArguments ex = new DefaultApplicationArguments(args);
// 创建和配置environment,发布事件:SpringApplicationRunListeners#environmentPrepared
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, ex);
this.configureIgnoreBeanInfo(environment);
// 打印SpringBoot的banner和版本
Banner printedBanner = this.printBanner(environment);
// 创建对应的ApplicationContext:Web类型,Reactive类型,普通的类型(非Web)
context = this.createApplicationContext();
exceptionReporters1 = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, new Object[]{context});
// 准备ApplicationContext,Initializers设置到ApplicationContext后发布事件:ApplicationContextInitializedEvent
// 打印启动日志,打印profile信息(如dev, test, prod)
// 调用EventPublishingRunListener发布ApplicationContext加载完毕事件:ApplicationPreparedEvent
this.prepareContext(context, environment, listeners, ex, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, ex);
stopWatch.stop();
if(this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
// 发布SprintBoot启动事件:ApplicationStartingEvent
listeners.started(context);
this.callRunners(context, ex);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
// 最后发布就绪事件ApplicationReadyEvent,标志着SpringBoot可以处理就收的请求了
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters1, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
经典的观察者模式,只要你把事件广播的顺序理解了,那整个流程就很容易串起来了:
1、创建一个StopWatch实例,用来记录SpringBoot的启动时间
2、通过SpringFactoriesLoader加载listeners:比如EventPublishingRunListener
3、发布SprintBoot开始启动事件(EventPublishingRunListener#starting())
4、创建和配置environment(environmentPrepared())
5、打印SpringBoot的banner和版本
6、创建对应的ApplicationContext:Web类型,Reactive类型,普通的类型(非Web)
7、repareContext
(1)准备ApplicationContext,Initializers设置到ApplicationContext(contextPrepared())
(2)打印启动日志,打印profile信息(如dev, test, prod)
(3)最终会调用到AbstractApplicationContext#refresh方法,实际上就是Spring IOC容器的创建过程,并且会进行自动装配的操作,以及发布 ApplicationContext已经refresh事件,标志着ApplicationContext初始化完成(contextLoaded())
8、afterRefresh hook方法
9、stopWatch停止计时,日志打印总共启动的时间
10、发布SpringBoot程序已启动事件(started())
11、调用ApplicationRunner和CommandLineRunner
12、最后发布就绪事件ApplicationReadyEvent,标志着SpringBoot可以处理就收的请求了(running())
最后
SpringApplicationRunListeners的唯一实现是EventPublishingRunListener;
整个SpringBoot的启动,流程就是各种事件的发布,调用EventPublishingRunListener中的方法。
只要明白了EventPublishingRunListener中事件发布的流程,也就明白了SpringBoot启动的大体流程
本文只是粗略理解,后续会出一半详细流程图
注意:本文归作者所有,未经作者允许,不得转载