源码分析基于spring boot 2.1
本文通过阅读源码,分析SpringBoot的启动过程。
(源码解析类文章建议在PC端阅读)
先看一个例子
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args); // #1
}
}
例子很简单,本文主要关注三个问题
-
SpringApplication#run方法的作用
-
SpringApplication#run方法中MyApplication.class参数的作用
-
SpringApplication#run方法中args参数的作用
SpringApplication#run
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
先构造SpringApplication实例,再调用run方法
SpringApplication#构造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // #1
this.webApplicationType = WebApplicationType.deduceFromClasspath(); //#2
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //#3
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //#4
this.mainApplicationClass = deduceMainApplicationClass(); // #5
}
#1
设置SpringApplication#primarySources,注意这里primarySources参数就是run方法的第一个参数#2
判断当前应用是JAVA应用,SERVLET应用或REACTIVE应用。#3
加载spring.factories中配置的ApplicationContextInitializer实现类,将结果存放到SpringApplication#initializers#4
加载spring.factories中配置的ApplicationListener实现类,将结果存放到SpringApplication#listeners#5
获取main方法所在Class
SpringApplication#getSpringFactoriesInstances -> SpringFactoriesLoader#loadSpringFactories
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); //#1
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource); //#2
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim()); //#3
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
#1
FACTORIES_RESOURCE_LOCATION就是字符串"META-INF/spring.factories",这里读取jar中META-INF/spring.factories文件内容#2
加载spring.factories文件(格式为Properties)#3
读取Properties内容,缓存结果
spring.factories格式为
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
key为spring扩展接口(或声明功能的注解),value为对应的功能实现类的列表
SpringApplication#run
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch(); // #1
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty(); // #2
SpringApplicationRunListeners listeners = getRunListeners(args); // #3
listeners.starting(); // #4
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // #5
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // #6
configureIgnoreBeanInfo(environment); // #7
Banner printedBanner = printBanner(environment);
context = createApplicationContext(); // #8
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner); // #9
refreshContext(context); // #10
afterRefresh(context, applicationArguments); // #11
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context); // #12
callRunners(context, applicationArguments); // #13
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context); // #14
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
#1
开始计时(计算初始化需要花费多少时间)#2
配置java.awt.headless#3
获取spring.factories中配置的SpringApplicationRunListener实现类
SpringApplicationRunListeners是SpringApplicationRunListener的组合,SpringApplicationRunListener是SpringBoot中新增接口,通过它来间接调用 ApplicationListener。
该接口实现类为EventPublishingRunListener,他的构造方法中需传入SpringApplication,并获取SpringApplication#listeners属性。
SpringApplicationRunListener组件很重要,SpringBoot中很多扩展也是通过listerner实现的,如日志系统的启动#4
发送ApplicationStartedEvent事件#5
命令行参数处理#6
构建Environment#7
处理spring.beaninfo.ignore配置#8
创建ApplicationContext#9
prepareContext#10
refreshContext#11
afterRefresh,预留扩展方法#12
发送ApplicationStartedEvent事件#13
运行ApplicationRunner,CommandLineRunner#14
发送ApplicationReadyEvent事件
SpringApplication#prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
ConfigurableEnvironment environment = getOrCreateEnvironment(); // #1
configureEnvironment(environment, applicationArguments.getSourceArgs()); // #2
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(environment); // #3
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
#1
创建一个Environment#2
将SpringApplication#run中的可变参数列表传递给Environment
回到文章开头说的第3个问题,关于MyApplication#run中的args参数
我们在启动SpringBoot时,可以添加命令行参数,如java -jar MyApplication.jar --spring.profiles.active=dev
命令行参数--spring.profiles.active=dev
会传递给main方法,main方法中需要将其传递给SpringApplication#run方法,
这里将命令行参数添加Environment中,作为一个PropertySource。
必须在main方法中将args参数传给SpringApplication#run方法,否则会造成命令行的参数失效。#3
发送ApplicationEnvironmentPreparedEvent事件
SpringApplication#createApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); // #1
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); // #2
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); // #3
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); // #4
}
#1
SERVLET应用,选择AnnotationConfigServletWebServerApplicationContext#2
REACTIVE应用,选择AnnotationConfigReactiveWebServerApplicationContext#3
JAVA应用,选择AnnotationConfigApplicationContext#4
构造对应的Spring Context
AnnotationConfigServletWebServerApplicationContext#构造方法 -> AnnotatedBeanDefinitionReader#构造方法 -> AnnotationConfigUtils#registerAnnotationConfigProcessors
这里注册一些实现SpringBoot功能必须的PostProcessor
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null) {
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
}
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); //#1
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); //#2
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
...
return beanDefs;
}
#1
注册ConfigurationClassPostProcessor,该PostProcessor处理@Configuration等注解#2
注册AutowiredAnnotationBeanPostProcessor,该PostProcessor处理@Value,@Autowired等注解
SpringApplication#prepareContext
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context); //#1
listeners.contextPrepared(context); //#2
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");
load(context, sources.toArray(new Object[0])); //#3
listeners.contextLoaded(context); //#4
}
#1
调用ApplicationContextInitializer#initialize#2
调用SpringApplicationRunListener#contextPrepared方法#3
getAllSources获取primarySources以及所有的PropertySource,并解析为BeanDefinition注册到Spring上下文中(后面要使用)
PropertySource(属性源)用于Environment重新获取配置属性。
primarySources就是SpringApplication#run方法的第一个参数
回到文章开头第2个问题,
通过SpringApplication#run方法的MyApplication.class参数,这里将MyApplication的BeanDefinition注册到Spring上下文中,后面Spring就是可以获取MyApplication上的@SpringBootApplication等注解了。#4
发送ApplicationContextInitializedEvent事件
SpringApplication#refreshContext
之前spring源码解析的文章已经说过refreshContext操作了。
SpringApplication#callRunners
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); // #1
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) { // #2
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
#1
获取ApplicationRunner和CommandLineRunner#2
调用对应的run方法