内容简介:调用SpringApplication.run方法先执行SpringApplication的构造方法,进行初始化动作,包括:接下来看一下run做了什么
@Slf4j
@EnableDiscoveryClient
@SpringBootApplication
public class KeplerPostLoanApplication {
/**
* 项目启动类
*
* @param args 启动参数
*/
public static void main(String[] args) {
SpringApplication.run(KeplerPostLoanApplication.class, args)
}
}
复制代码
调用SpringApplication.run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
复制代码
先执行SpringApplication的构造方法,进行初始化动作,包括:
- 1、识别webApplicationType类型
- 2、识别mainApplicationClass类
- 3、加载META-INF/spring.factories文件中定义的定义的 ApplicationContextInitializer、ApplicationListener
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
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();
// 加载META-INF/spring.factories文件中,定义的ApplicationContextInitializer
setInitializers((Collection)getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 加载META-INF/spring.factories文件中,定义的ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
复制代码
接下来看一下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 stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 获取SpringApplicationRunListener
// 默认只有EventPublishingRunListener,用来结合spring启动流程,发布SpringApplicationEvent
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发布ApplicationStartingEvent,例如
// BackgroundPreinitializer
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
// 子类扩展
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
// 发布ApplicationStartedEvent,例如
// TomcatMetricsBinder
listeners.started(context);
// 回调ApplicationRunner、CommandLineRunner,例如
// JobLauncherCommandLineRunner,启动jobLauncher
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
复制代码
归纳下SpringApplication.run方法的关键动作
- 第一步:构造容器环境(prepareEnvironment)
- 第二步:创建容器(createApplicationContext)
- 第三步:准备容器(prepareContext)
- 第四步:刷新容器(refreshContext)
- 第五步:刷新容器后的扩展接口(afterRefresh)
- 第六步:回调Runner类(callRunners)
其中:执行过程中,通过SpringApplicationRunListener的实现类EventPublishingRunListener在对应动作的时间点,Spring启动事件。
附上,springApplicationEvent事件列表
主要以上6步,下面具体分析。
第一步:构造容器环境
/** SpringApplication.java **/
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 1、初始化environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 2、加载默认配置,defaultProperties、springApplicationCommandLineArgs
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 3.发布ApplicationEnvironmentPreparedEvent,例如
// BootstrapApplicationListener,创建bootstrapContext上下文,加载bootstrap.properties
// ConfigFileApplicationListener,加载YamlProperty文件与PropertiesProperty文件,添加PropertySourceOrderingPostProcessor
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
复制代码
首先getOrCreateEnvironment方法实现如下: new 一个StandardXXXEnvironment(),其中,构造函数中会执行customizePropertySources方法,加载对应的PropertySource。
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
复制代码
附上StandardEnvironment的customizePropertySources实现
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
// 属性获取时,for each propertySources,先匹配到就返回,
// 所以systemProperties(如:java -Dsource=xxx -jar xxx.jar)优先级高于systemEnvironment(环境变量)
propertySources.addLast(new MapPropertySource("systemProperties", getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", getSystemEnvironment()));
}
复制代码
第二步:创建容器
实际上,就是new AnnotationConfigServletWebServerApplicationContext()
// SpringApplication.java
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
复制代码
重点关注:构造方法中, 创建了AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner两个对象
// AnnotationConfigServletWebServerApplicationContext.java
public AnnotationConfigServletWebServerApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
复制代码
其中,AnnotatedBeanDefinitionReader 创建过程中,注册了多个十分重要的BeanPostProcessor,包括处理@Configuration注解的ConfigurationClassPostProcessor等。
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
// Register all relevant annotation post processors in the given registry,包括:
// ConfigurationClassPostProcessor
// AutowiredAnnotationBeanPostProcessor
// RequiredAnnotationBeanPostProcessor
// CommonAnnotationBeanPostProcessor
// PersistenceAnnotationBeanPostProcessor
// EventListenerMethodProcessor
// DefaultEventListenerFactory
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
复制代码
第三步:准备容器
/** SpringApplication.java **/
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
// 调用ApplicationContextInitializer.initialize(),例如:
// EnvironmentDecryptApplicationInitializer
// PropertySourceBootstrapConfiguration
// SharedMetadataReaderFactoryContextInitializer,注入SharedMetadataReaderFactoryBean
applyInitializers(context);
// ApplicationContextInitializedEvent,暂无例子
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 加载springBoot启动类注入到spring容器中bean map中
// AnnotatedBeanDefinitionReader.doRegisterBean()
load(context, sources.toArray(new Object[0]));
// 发布ApplicationPreparedEvent
// ConfigFileApplicationListener,注入PropertySourceOrderingPostProcessor,调整defaultProperties到尾部
listeners.contextLoaded(context);
}
复制代码
第四步:刷新容器
refresh方法在spring整个源码体系中举足轻重,后续单独讲解。
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
复制代码
第五步:刷新容器后的扩展接口(afterRefresh)
protected void afterRefresh(ConfigurableApplicationContext context,
ApplicationArguments args) {
}
复制代码
扩展接口,设计模式中的模板方法,默认为空实现,子类扩展。
第六步:回调Runner类(callRunners)
容器就绪后,触发回调动作,目前见到的实现有 JobLauncherCommandLineRunner,启动jobLauncher
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
复制代码
本章节常用扩展接口以及使用案例
执行顺序(重点关注):
- ApplicationStartingEvent
- ApplicationEnvironmentPreparedEvent
- ApplicationContextInitializer
- ApplicationContextInitializedEvent
- ApplicationPreparedEvent
- ApplicationStartedEvent
- ApplicationRunner、CommandLineRunner
- ApplicationReadyEvent
使用案例:
1. ApplicationStartingEvent
- 简单描述:程序启动时,最早触发的动作
- 应用场景:
- ApplicationPidFileWriter(saves application PID into file)
- LoggingApplicationListener(configures the {@link LoggingSystem})
2. ApplicationEnvironmentPreparedEvent
- 简单描述: 创建环境Environment完成,并且已经载默认的配置,例如systemEnvironment、systemProperties
- 应用场景:比较多的是加载自定义的配置、spring Cloud也通过这个事件,去初始化Cloud上下文。
- BootstrapApplicationListener:初始化BootstrapApplicationContext上下文,包括加载bootstrap.properties、载入spring.factories 定义的BootstrapConfiguration
- ConfigFileApplicationListener:加载application.properties、application.yml
以上所述就是小编给大家介绍的《springBoot2.1.5启动源码解析(一)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- ReactNative源码解析-初识源码
- Spring源码系列:BeanDefinition源码解析
- Spring源码分析:AOP源码解析(下篇)
- Spring源码分析:AOP源码解析(上篇)
- 注册中心 Eureka 源码解析 —— EndPoint 与 解析器
- 新一代Json解析库Moshi源码解析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
信息检索导论
Christopher D.Manning、Hinrich Schütze、Prabhakar Raghavan / 王斌 / 人民邮电出版社 / 201008 / 69.00元
封面图片为英国伯明翰塞尔福瑞吉百货大楼,其极具线条感的轮廓外型优美,犹如水波的流动。其外表悬挂了1.5万个铝碟,创造出一种极具现代气息的纹理装饰效果,有如夜空下水流的波光粼粼,闪烁于月光之下,使建筑的商业氛围表现到极致。设计该建筑的英国“未来系统建筑事物所”,将商场内部围合成一个顶部采光的中庭,配以交叉的自动扶梯,使购物环境呈现出一种凝聚的向心力和商业广告的展示效应。作为英国第二商业城市伯明翰的建......一起来看看 《信息检索导论》 这本书的介绍吧!