[springboot]springboot启动流程[通俗易懂]
大家好,又见面了,我是你们的朋友全栈君。
Spring Boot程序有一个入口,就是main方法。main里面调用SpringApplication.run()启动整个Spring Boot程序,该方法所在类需要使用@SpringBootApplication复合注解。
1、@SpringBootApplication注解的组成
@EnableAutoConfiguration
@EnableAutoConfiguration是借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器
在这个注解中,最重要的是它导入了一个类EnableAutoConfigurationImportSelector,它是一个ImportSelector接口的实现类
Spring Boot在启动时,在classpath下所有的META-INF/spring.factories文件中根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfig.EnableAutoConfiguration作为查找的Key,获得对应的一组@Configuration类,并将其封装到一个List中返回。(spring.factories是一个典型的java properties文件,只不过Key和Value都是Java类型的完整类名)
通过 filter 过滤掉当前环境不需要自动装配的类,比如没有集成RabbitMQ,就不需要,或者有的条件@Conditional不满足也不需要自动装配
将需要自动装配的全路径类名注册到 SpringIOC 容器,自此 SpringBoot 自动装配完成!
@SpringBootConfiguration
该注解作用就是将当前的类作为一个JavaConfig,然后触发注解@EnableAutoConfiguration和@ComponentScan的处理,本质上与@Configuration注解没有区别。
@ComponentScan
@ComponentScan这个注解在Spring中很重要,它对应XML配置中的元素,@ComponentScan的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。
我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。
注:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。
2、SpringBoot启动流程
1、SpringBoot启动的时候,会构造一个SpringApplication的实例,然后调用这个实例的run方法,在run方法调用之前,也就是构造SpringApplication的时候会进行初始化的工作,初始化的时候会做以下几件事:
- 把参数sources设置到SpringApplication属性中,这个sources可以是任何类型的参数.
- 判断是否是web程序,并设置到webEnvironment的boolean属性中.
- 创建并初始化ApplicationInitializer,设置到initializers属性中 。
- 创建并初始化ApplicationListener,设置到listeners属性中 。
- 初始化主类mainApplicatioClass。
源代码:
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
//把sources设置到SpringApplication的sources属性中,目前只是一个MyApplication类对象
this.sources.addAll(Arrays.asList(sources));
}
//判断是否是web程序,并设置到webEnvironment的boolean属性中
this.webEnvironment = deduceWebEnvironment();
//找出所有的初始化器,默认有5个,设置到initializers属性中 。
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//找出所有的应用程序监听器,默认有9个,设置到listeners属性中 。
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//找出运行的主类(main class)
this.mainApplicationClass = deduceMainApplicationClass();
}
2、SpringApplication构造完成之后调用run方法,启动SpringApplication,run方法执行的时候会做以下几件事:
- 构造一个StopWatch计时器,用来记录SpringBoot的启动时间 。
- 获取SpringApplicationRunListeners并封装到SpringApplicationRunListeners中启动,用于监听run方法的执行。
- 创建并初始化ApplicationArguments,获取run方法传递的args参数。
- 创建并初始化ConfigurableEnvironment(环境配置)。
- 打印banner和版本。
- 构造Spring容器(ApplicationContext)上下文。
- SpringApplicationRunListeners发布finish事件。
- StopWatch计时器停止计时,日志打印总共启动的时间。
- 发布SpringBoot程序已启动事件(started())
- 调用ApplicationRunner和CommandLineRunner
- 最后发布就绪事件ApplicationReadyEvent,标志着SpringBoot可以处理就收的请求了(running())
public ConfigurableApplicationContext run(String... args) {
// 创建一个StopWatch实例,用来记录SpringBoot的启动时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 通过SpringFactoriesLoader加载listeners:比如EventPublishingRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发布SprintBoot启动事件:ApplicationStartingEvent
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 创建和配置environment,发布事件:SpringApplicationRunListeners#environmentPrepared
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 打印SpringBoot的banner和版本
Banner printedBanner = printBanner(environment);
// 创建对应的ApplicationContext:Web类型,Reactive类型,普通的类型(非Web)
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] {
ConfigurableApplicationContext.class }, context);
// 准备ApplicationContext,Initializers设置到ApplicationContext后发布事件:ApplicationContextInitializedEvent
// 打印启动日志,打印profile信息(如dev, test, prod)
// 调用EventPublishingRunListener发布ApplicationContext加载完毕事件:ApplicationPreparedEvent
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 最终会调用到AbstractApplicationContext#refresh方法,实际上就是Spring IOC容器的创建过程,并且会进行自动装配的操作
// 以及发布ApplicationContext已经refresh事件,标志着ApplicationContext初始化完成
refreshContext(context);
// hook方法
afterRefresh(context, applicationArguments);
// stopWatch停止计时,日志打印总共启动的时间
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 发布SpringBoot程序已启动事件ApplicationStartedEvent
listeners.started(context);
// 调用ApplicationRunner和CommandLineRunner
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 最后发布就绪事件ApplicationReadyEvent,标志着SpringBoot可以处理就收的请求了
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/171209.html原文链接:https://javaforall.cn