SpringBoot到底run了什么
我们详细描述了SpringApplication对象实例的创建过程,本篇文章继续看
run
方法的执行逻辑吧
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//后面还有,本篇文章就解析到这。。。。
}
- 第一行使用了
StopWatch
来记录开始时间 - 设置了
java.awt.headless
环境变量,在网上了解了一下这个变量的相关信息
Headless模式是系统的一种配置模式。在系统可能缺少显示设备、键盘或鼠标这些外设的情况下可以使用该模式
个人理解为是一些图形相关的组件能否使用的开关,欢迎各位大佬指正
- 接着遍历所有构造
SpringApplication
实例时加载的SpringApplicationRunListener
,调用它们的started
方法
这里构造时仅仅加载了一个EventPublishingRunListener
类,所以咱们就来解析一下这个东东
public void starting() {
this.initialMulticaster.multicastEvent(
new ApplicationStartingEvent(this.application, this.args));
}
可以看到这里调用了SimpleApplicationEventMulticaster
类的multicastEvent
方法并且传入了ApplicationStartingEvent
对象,看名字就知道了这个是SpringBoot启动事件
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
其中获取监听器使用的是getApplicationListeners
方法,这个方法中主要就是从最启动时获取的所有监听器和这个事件做了下匹配,返回通过匹配的监听器集合
接着就是看是否设置线程池参数,如果有线程池则使用线程池的线程进行操作,否则将同步调用监听器
-
把所有的命令行启动参数封装成
ConfigurableEnvironment
对象 -
准备运行时环境
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
return environment;
}
获取或创建环境getOrCreateEnvironment
方法名就很直观,有就直接获取,没有就新建
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
if (this.webApplicationType == WebApplicationType.SERVLET) {
return new StandardServletEnvironment();
}
return new StandardEnvironment();
}
上篇文章中说过了,咱们是Servlet环境,所以当前方法是返回一个StandardServletEnvironment
对象,这个对象的构造过程中调用了customizePropertySources
方法(它父类的父类调用的)
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
propertySources.addLast(new StubPropertySource("servletContextInitParams"));
if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource("jndiProperties"));
}
super.customizePropertySources(propertySources);
}
//这是它父类的
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new MapPropertySource("systemProperties", getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", getSystemEnvironment()));
}
可以看出StandardServletEnvironment
往propertySources
中添加了两个StubPropertySource
对象,而它的父类添加了一个包含java系统属性和一个操作系统环境变量的对象
配置 configureEnvironment
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
// 配置PropertySources
configurePropertySources(environment, args);
// 配置Profiles
configureProfiles(environment, args);
}
分别看一下两个方法
配置PropertySources
protected void configurePropertySources(ConfigurableEnvironment environment,
String[] args) {
MutablePropertySources sources = environment.getPropertySources();
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
// 存在默认配置将其放到最后位置
sources.addLast(
new MapPropertySource("defaultProperties", this.defaultProperties));
}
// 如果存在命令行参数则将原有的替换掉
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(new SimpleCommandLinePropertySource(
"springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
// 将其放到第一位置
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
这里就体现出了这个命令行参数比应用配置文件的优先级高的情况了
配置Profiles
从PropertySources中查找spring.profiles.active属性,存在则将其值添加activeProfiles集合中
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
environment.getActiveProfiles();
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
发布EnvirongmentPreparedEvent
事件
绑定环境
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
try {
Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
}
catch (Exception ex) {
throw new IllegalStateException("Cannot bind to SpringApplication", ex);
}
}
转换环境
如果web环境变更为NONE则将StandardServletEnvironment
转换为StandardEnvironment
ConfigurationPropertySources.attach(environment)
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
MutablePropertySources sources = ((ConfigurableEnvironment) environment)
.getPropertySources();
PropertySource<?> attached = sources.get("configurationProperties");
if (attached != null && attached.getSource() != sources) {
sources.remove("configurationProperties");
attached = null;
}
if (attached == null) {
sources.addFirst(new ConfigurationPropertySourcesPropertySource(
"configurationProperties",
new SpringConfigurationPropertySources(sources)));
}
}
最终这个sources
对象的第一个位置放的是它自己,循环引用,这个具体的含义
public ConfigurableApplicationContext run(String... args) {
//。。。
//接上文继续
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);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
listeners.running(context);
return context;
}
- 获取系统属性
spring.beaninfo.ignore
private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
if (System.getProperty(
CachedIntrospectionResults."spring.beaninfo.ignore") == null) {
Boolean ignore = environment.getProperty("spring.beaninfo.ignore",
Boolean.class, Boolean.TRUE);
System.setProperty(CachedIntrospectionResults."spring.beaninfo.ignore",
ignore.toString());
}
}
但是这个属性的作用还真不知道。。
-
打印banner
-
根据当前环境创建ApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_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);
}
基于咱们的Servlet环境,所以创建的ApplicationContext为AnnotationConfigServletWebServerApplicationContext
- 加载
SpringBootExceptionReporter
,这个类里包含了SpringBoot启动失败后异常处理相关的组件
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
- prepareContext 这一块还是比较长的
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
- 第一行,将context中相关的environment全部替换
public void setEnvironment(ConfigurableEnvironment environment) {
super.setEnvironment(environment); // 设置context的environment
this.reader.setEnvironment(environment); // 实例化context的reader属性的conditionEvaluator属性
this.scanner.setEnvironment(environment); // 设置context的scanner属性的environment属性
}
- 上下文后处理
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context)
.setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context)
.setClassLoader(this.resourceLoader.getClassLoader());
}
}
}
这一块默认beanNameGenerator
和resourceLoader
都是空的,只有当我们自定义这两个对象时才会把容器内的bean替换
- 执行所有的
ApplicationContextInitializer
的initialize
方法
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
listeners.contextPrepared(context)
这是个空方法,没有实现,一个Spring的扩展点- 打印profile
- 注册bean:
springApplicationArguments
- 发布事件
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));
}
这里不仅发布了ApplicationPreparedEvent
事件,还往实现了ApplicationContextAware
接口的监听器中注入了context容器
- load,其实就是创建了一个
BeanDefinitionLoader
对象
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug(
"Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
BeanDefinitionLoader loader = createBeanDefinitionLoader(
getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
loader.load();
}
- 容器的初始化
refreshContext
这个方法最后还是调用的AbstractApplicationContext
类的refresh
方法,由于篇幅过长这里就不展开了,感兴趣的同学可以参考这篇文章:基于注解的SpringIOC源码解析
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 记录容器的启动时间、标记“已启动”状态、检查环境变量
prepareRefresh();
// 初始化BeanFactory容器、注册BeanDefinition
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
prepareBeanFactory(beanFactory);
try {
// 扩展点
postProcessBeanFactory(beanFactory);
// 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
invokeBeanFactoryPostProcessors(beanFactory);
// 注册 BeanPostProcessor 的实现类
registerBeanPostProcessors(beanFactory);
// 初始化MessageSource
initMessageSource();
// 初始化事件广播器
initApplicationEventMulticaster();
// 扩展点
onRefresh();
// 注册事件监听器
registerListeners();
// 初始化所有的 singleton beans
finishBeanFactoryInitialization(beanFactory);
// 广播事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 销毁已经初始化的的Bean
destroyBeans();
// 设置 'active' 状态
cancelRefresh(ex);
throw ex;
}
finally {
// 清除缓存
resetCommonCaches();
}
}
}
-
afterRefresh
这里没有任何实现,Spring留给我们的扩展点 -
停止之前启动的计时装置,然后发送
ApplicationStartedEvent
事件 -
调用系统中
ApplicationRunner
以及CommandLineRunner
接口的实现类,关于这两个接口的使用可以参考我的这篇文章:Java项目启动时执行指定方法的几种方式
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);
}
}
}
-
异常处理
-
发送
ApplicationReadyEvent
事件