SpringBoot原理发现(三)
说明:
本系列基于SpringBoot 2.2.9.RELEASE 版本,对SpringBoot的原理进行分析,一共分为四节:
SpringBoot原理发现(一):创建Hello World,对pom依赖以及@SpringBootApplication注解进行分析
SpringBoot原理发现(二):分析SpringBoot自动配置原理
SpringBoot原理发现(三):通过主配置类main方法分析SpringBoot启动配置原理
SpringBoot原理发现(四):了解SpringBoot启动中的几个重要回调机制
SpringBoot 启动配置原理
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }
启动运行流程分析
debug上面主配置类的run方法,它会执行到如下代码:
由此可见,主配置类run方法分为两个部分。
第一步 :new SpringApplication(primarySources),创建出SrpingApplication对象
第二步 :调用返回对象SpringApplication.run(args)
1. new SpringApplication(primarySources)
跟踪new SpringApplication(primarySources),最终会执行SpringApplication的构造方法,如下:
根据方法形参可以看出配置类其实可以传入多个,构造函数执行流程解释如下:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; //检查主要来源类是否为空,此时就是我们传入的主配置类:class com.example.demo.DemoApplication Assert.notNull(primarySources, "PrimarySources must not be null"); //将主要来源类保存到primarySources中,primarySources:Set<Class<?>> primarySources this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //判断当前应用的类型,返回WebApplicationType.SERVLET,代表是一个web应用 this.webApplicationType = WebApplicationType.deduceFromClasspath(); /** * 查找类路径下META-INF/spring.factories 文件中定义的ApplicationContextInitializer的值列表并保存在initializers属性中 * * 其实和 Springboot原理探究(一) * 2.2.2 @Import(AutoConfigurationImportSelector.class) 同理 * 只要是getSpringFactoriesInstances()就是去查找类路径下META-INF/spring.factories文件指定的值列表 */ setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //同上面一样,查找类路径下META-INF/spring.factories 文件中定义的ApplicationListener的值列表并保存在listeners属性中 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //查找配置类中有main方法的主配置类 this.mainApplicationClass = deduceMainApplicationClass(); }
META-INF/spring.factories
# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer
至此new SpringApplication(primarySources)执行完毕,并创建出SpringApplication对象 ,可以看出此步骤主要是将 ApplicationContextInitializer 和 ApplicationListener 保存起来
2. springApplication.run(args)
调用的run方法解释如下:
/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { //StopWatch开始监听,设置IOC容器属性null StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); //AWT应用,忽略 configureHeadlessProperty(); //获取类路径下META-INF/spring.factories 文件中SpringApplicationRunListener定义的值,参见下面 2.1 SpringApplicationRunListeners listeners = getRunListeners(args); //回调所有SpringApplicationRunListener的starting(),可以查看SpringApplicationRunListener监听器中定义的几个回调方法 ,参见下面 2.2 listeners.starting(); try { //将args封装成ApplicationArguments对象 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //准备环境,并回调SpringApplicationRunListener的environmentPrepared方法,参见下面 2.3 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); //打印Spring Banner图 Banner printedBanner = printBanner(environment); //通过BeanUtils反射创建IOC容器,此处会根据this.webApplicationType判断是否为web环境,而this.webApplicationType已经在第一大步骤中得到 context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); /** 准备上下文:将environment保存在IOC容器中,
* 并 回调所有的ApplicationContextInitializer的initialize方法, ApplicationContextInitializer则是在第一大步骤中已经获取且保存了 * 回调所有SpringApplicationRunListener的contextPrepared方法, * 回调所有SpringApplicationRunListener的contextLoaded方法
* 参见下面 2.4
*/ prepareContext(context, environment, listeners, applicationArguments, printedBanner); //刷新容器:初始化IOC,初始化所有的组件,如果是web应用此处还会创建嵌入式的tomcat refreshContext(context); //2.2.9.RELEASE版本的spingboot这就是一个空方法,之前的版本中会调用callRunners方法 afterRefresh(context, applicationArguments); //StopWatch停止监听 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } //调用所有SpringApplicationRunListener的started方法 listeners.started(context); //从IOC容器中获取ApplicationRunner 和 CommandLineRunner, SpringBoot中比较重要的几个回调机制
//先回调ApplicationRunner的run在回调CommandLineRunner callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { //回调所有SpringApplicationRunListener的running方法 listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } //返回IOC容器 return context; }
2.1 SpringApplicationRunListener 监听器
SpringBoot中比较重要的回调机制之一
可以看出又是getSpringFactoriesInstances方法,只要是getSpringFactoriesInstances()就是去查找类路径下META-INF/spring.factories文件指定的值列表,而这里就是去获取SpringApplicationRunListener的值列表
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)); }
2.2 SpringApplicationRunListener监听器
回调SpringApplicationRunListener 的 starting方法
void starting() { for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); } }
2.3 prepareEnvironment(listeners, applicationArguments) 环境准备
回调SpringApplicationRunListener 的 environmentPrepared方法
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 创建环境 ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); //回调SpringApplicationRunListener的environmentPrepared方法,环境准备完成 listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }
2.4 准备上下文
回调之前已经保存的所有ApplicationContextInitializer的initialize方法
回调所有SpringApplicationRunListener的contextPrepared方法
回调所有SpringApplicationRunListener的contextLoaded方法
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); //回调所有的ApplicationContextInitializer的initialize方法,而ApplicationContextInitializer就是之前创建SpringApplication时保存的 applyInitializers(context); //回调所有SpringApplicationRunListener的contextPrepared方法 listeners.contextPrepared(context); //日志记录 if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); //回调所有SpringApplicationRunListener的contextLoaded方法 listeners.contextLoaded(context); }
至此springApplication.run(args)执行完毕,可以看出第二大步骤就是环境准备,初始化IOC容器,扫描Bean创建组件,并调用几个重要的回调机制。
SpringBoot中四个重要的回调机制
1. ApplicationContextInitializer
在第一大步骤中扫描META-INF/spring.factories 文件得到,在第二大步骤中回调其initialize方法
2. SpringApplicationRunListener
在第二大步骤中扫描META-INF/spring.factories 文件得到,并依次回调
a. starting()
b. environmentPrepared()
c. contextPrepared()
d. contextLoaded()
e. started()
f. running()
3. ApplicationRunner
在第二大步骤中,通过IOC容器获取,且回调run方法
4. CommandLineRunner
在第二大步骤中,通过IOC容器获取,且回调run方法