几年前初出茅庐时,获得了一个心仪大厂的面试邀约,面试前准备万分,即使信心十足,面试当场还是有些紧张,导致发挥不如预期,至今仍有些许遗憾。(*>﹏<*)
这个【SpringBoot启动流程】就是当初没回答好的部分,这次来补上,机会虽然错过,但知识绝不能错过,以下整理相关知识点。
这边先简单扼要叙述一下SpringBoot的强大之处:
让我们先来看看SpringBoot的启动流程图,如下图所示:
用过SpringBoot的技术人员很显而易见的两者之间的差别就是视觉上很直观的:SpringBoot有自己独立的启动类(独立程序)
@SpringBootApplication
public class ServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
}
这段代码的核心为注解定义(@SpringBootApplication)和类定义(SpringApplication.run)
注解内部源码如下:
@Target({ElementType.TYPE}) // 注解的适用范围,其中TYPE用于描述类、接口(包括包注解类型)或enum声明
@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期,保留到class文件中(三个生命周期)
@Documented // 表明这个注解应该被javadoc记录
@Inherited // 子类可以继承该注解
@SpringBootConfiguration // 继承了Configuration,表示当前是注解类
@EnableAutoConfiguration // 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助
@ComponentScan(excludeFilters = {@Filter( // 扫描路径设置
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
...
}
在其中比较重要的有三个注解,分别是:
下面先来说说第一个注解:
根据Javadoc可知,该注解作用就是将当前的类作为一个JavaConfig,然后触发注解@EnableAutoConfiguration和@ComponentScan的处理,本质上与@Configuration注解没有区别。
Java Configuration的配置形式注入bean,参考以下代码:
@Configuration
public class FakeConfiguration{
@Bean
public FakeService mockService(){
return new FakeServiceImpl();
}
}
@Configuration标注当前类是Java Config配置类,会被扫描并加载到IoC容器。
这边额外简单补充一下 @Configuration 和 @Component的区别:
此注解顾名思义是可以自动配置,所以应该是springboot中最为重要的注解。
在spring框架中就提供了各种以@Enable开头的注解,例如: @EnableScheduling、@EnableCaching、@EnableMBeanExport等; @EnableAutoConfiguration的理念和做事方式其实一脉相承简单概括一下就是,借助@Import的支持,收集和注册特定场景相关的bean定义。
@EnableAutoConfiguration也是借助@Import的帮助,将所有符合自动配置条件的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 {};
}
还有其中比较重要的一个类就是:AutoConfigurationImportSelector.class
SpringBoot 自动装配的核心注解,在 Spring 框架中就提供了各种以 @Enable 开头的注解,例如: @EnableCircuitBreaker、@EnableScheduling 等;
main方法里调用org.springframework.boot.SpringApplication.run(java.lang.Class>, java.lang.String…)方法。
调用另外一个同名的重载方法run
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
经典的观察者模式:
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;
}
方法说明如下:
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;
private final SimpleApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
@Override
public int getOrder() {
return 0;
}
// SpringBoot启动事件
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
// 创建和配置环境
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster
.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
// 准备ApplicationContext
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
this.initialMulticaster
.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}
// 发布ApplicationContext已经refresh事件,标志着ApplicationContext初始化完成
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}
// SpringBoot已启动事件
@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
}
// "SpringBoot现在可以处理接受的请求"事件
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
if (context != null && context.isActive()) {
// Listeners have been registered to the application context so we should
// use it at this point if we can
context.publishEvent(event);
}
else {
// An inactive context may not have a multicaster so we use our multicaster to
// call all of the context's listeners instead
if (context instanceof AbstractApplicationContext) {
for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
.getApplicationListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
this.initialMulticaster.multicastEvent(event);
}
}
private static class LoggingErrorHandler implements ErrorHandler {
private static final Log logger = LogFactory.getLog(EventPublishingRunListener.class);
@Override
public void handleError(Throwable throwable) {
logger.warn("Error calling ApplicationEventListener", throwable);
}
}
}
由于现在大都是用SpringBoot开发,所以呢,Spring IOC 初始化的源码,就是AnnotationConfigApplicationContext中的源码,IoC的初始化就是该类实例创建的过程。
创建的过程 (AnnotationConfigApplicationContext的构造方法):
已经工作了一段时间了,回头看看这些知识点,发现自己仍有许多不足之处,通过这次学习,不再让自己停留在“会用”的阶段,而是进一步理解其中原理,注重实践,自己还是得抓紧脚步跟上大佬的脚步。(☆_☆)