内容简介:调用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源码解析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Filter Bubble
Eli Pariser / Penguin Press / 2011-5-12 / GBP 16.45
In December 2009, Google began customizing its search results for each user. Instead of giving you the most broadly popular result, Google now tries to predict what you are most likely to click on. Ac......一起来看看 《The Filter Bubble》 这本书的介绍吧!