main方法进入
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
底下的方法分为两步:new SpringApplication(primarySources):创建SpringApplication
和run():启动SpringApplication
。
1.创建SpringApplication
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// resourceLoader为null
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 将传入的DemoApplication启动类放入primarySources中,这样应用就知道主启动类在哪里,叫什么了
// SpringBoot一般称呼这种主启动类叫primarySource(主配置资源来源)
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 判断当前应用环境[Reactive环境(全NIO)/ Servlet(WebMvc)环境/非Web环境]
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 1.1 设置初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 1.2 设置监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 1.3 确定主配置类
this.mainApplicationClass = deduceMainApplicationClass();
}
1.1 setInitializers:设置初始化器
它是在IOC容器之前的回调。它的使用方式有三种:
- 运行SpringApplication之前手动添加
public class ApplicationContextInitializerDemo implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("ApplicationContextInitializerDemo#initialize run...");
}
}
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
// SpringApplication.run(DemoApplication.class, args);
SpringApplication springApplication = new SpringApplication(DemoApplication.class);
springApplication.addInitializers(new ApplicationContextInitializerDemo());
springApplication.run(args);
}
}
- application.properties中配置
context.initializer.classes=com.example.demo.ApplicationContextInitializerDemo
- spring.factories中配置
在工程的 resources 目录下新建 “META-INF” 目录,并在下面创建一个 spring.factories 文件。在文件内声明:
org.springframework.context.ApplicationContextInitializer=com.example.demo.ApplicationContextInitializerDemo
内置Initializer
spring-boot 和 spring-boot-autoconfigure 包下的 spring.factories 里面对于 ApplicationContextInitializer 的配置:
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ // 报告IOC容器的一些常见的错误配置
org.springframework.boot.context.ContextIdApplicationContextInitializer,\ // 设置Spring应用上下文的ID
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ // 加载 application.properties 中 context.initializer.classes 配置的类
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer // 将内置servlet容器实际使用的监听端口写入到 Environment 环境属性中
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ // 创建一个 SpringBoot 和 ConfigurationClassPostProcessor 共用的 CachingMetadataReaderFactory 对象
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener // 将 ConditionEvaluationReport 写入日志
1.2 setListeners:设置监听器
用于监听IOC容器中发布的各种事件,到后续看IOC容器的刷新过程时可以看到。
// 加载所有类型为ApplicationListener的已配置的组件的全限定类名
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
内置Listener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\ // 应用上下文加载完成后对缓存做清除工作
org.springframework.boot.builder.ParentContextCloserApplicationListener,\ // 监听双亲应用上下文的关闭事件并往自己的子应用上下文中传播
org.springframework.boot.context.FileEncodingApplicationListener,\ // 检测系统文件编码与应用环境编码是否一致,如果系统文件编码和应用环境的编码不同则终止应用启动
org.springframework.boot.context.config.AnsiOutputApplicationListener,\ // 根据 spring.output.ansi.enabled 参数配置 AnsiOutput
org.springframework.boot.context.config.ConfigFileApplicationListener,\ // 从常见的那些约定的位置读取配置文件
org.springframework.boot.context.config.DelegatingApplicationListener,\ // 监听到事件后转发给 application.properties 中配置的 context.listener.classes 的监听器
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\ // 对环境就绪事件 ApplicationEnvironmentPreparedEvent 和应用失败事件 ApplicationFailedEvent 做出响应
org.springframework.boot.context.logging.LoggingApplicationListener,\ // 配置 LoggingSystem。使用 logging.config 环境变量指定的配置或者缺省配置
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener // 使用一个可以和 SpringBoot 可执行jar包配合工作的版本替换 LiquibaseServiceLocator
1.3 确定主配置类
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
// 从本方法开始往上爬,哪一层调用栈上有main方法,方法对应的类就是主配置类
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
源码很简单,从 deduceMainApplicationClass 方法开始往上爬,哪一层调用栈上有main方法,方法对应的类就是主配置类,就返回这个类。
2.run():启动SpringApplication
public ConfigurableApplicationContext run(String... args) {
// 创建StopWatch对象,用来监控启动时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 创建空的IOC容器,和一组异常报告器、配置与awt相关的信息
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 2.1 获取SpringApplicationRunListeners,并调用starting方法(回调机制)
SpringApplicationRunListeners listeners = getRunListeners(args);
// 【回调】首次启动run方法时立即调用。可用于非常早期的初始化(准备运行时环境之前)。
listeners.starting();
try {
// 将main方法的args参数封装到一个对象中
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 2.2 准备运行时环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 如果有配置 spring.beaninfo.ignore,则将该配置设置进系统参数
configureIgnoreBeanInfo(environment);
// 打印SpringBoot的banner
Banner printedBanner = printBanner(environment);
// 2.3 创建ApplicationContext
context = createApplicationContext();
// 初始化异常报告器
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 2.4 初始化IOC容器
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 2.5 刷新容器,最核心的部分
refreshContext(context);
// 刷新后的处理,空方法
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 【回调】发布started事件,事件的发布还会影响到父容器
listeners.started(context);
// 运行器回调:从容器中获取了ApplicationRunner和CommandLineRunner,已标记废弃
callRunners(context, applicationArguments);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);
throw new IllegalStateException(var9);
}
}
2.1 getRunListeners:获取SpringApplicationRunListeners
依然是SPI机制,取spring.factories中所有SpringApplicationRunListener。
/**
* Listener for the SpringApplication code run method.
* SpringApplicationRunListeners are loaded via the SpringFactoriesLoader
* and should declare a public constructor that accepts a SpringApplication
* instance and a String[] of arguments. A new
* SpringApplicationRunListener instance will be created for each run.
*
* 监听SpringApplication运行方法。
* SpringApplication是SpringFactoriesLoader,应该声明一个接受SpringApplication实例和String[]参数的公共构造函数。
* 将为每次运行创建一个新的SpringApplicationRunListener的instance。
*/
public interface SpringApplicationRunListener {
/**
* Called immediately when the run method has first started. Can be used for very
* early initialization.
* 首次启动run方法时立即调用。可用于非常早期的初始化。
*/
void starting();
/**
* Called once the environment has been prepared, but before the
* ApplicationContext has been created.
* 准备好环境(Environment构建完成),但在创建ApplicationContext之前调用。
*/
void environmentPrepared(ConfigurableEnvironment environment);
/**
* Called once the ApplicationContext has been created and prepared, but
* before sources have been loaded.
* 在创建和构建ApplicationContext之后,但在加载之前调用。
*/
void contextPrepared(ConfigurableApplicationContext context);
/**
* Called once the application context has been loaded but before it has been
* refreshed.
* ApplicationContext已加载但在刷新之前调用。
*/
void contextLoaded(ConfigurableApplicationContext context);
/**
* The context has been refreshed and the application has started but
* CommandLineRunners and ApplicationRunners have not been called.
* @since 2.0.0
* ApplicationContext已刷新,应用程序已启动,但尚未调用CommandLineRunners和ApplicationRunners。
*/
void started(ConfigurableApplicationContext context);
/**
* Called immediately before the run method finishes, when the application context has
* been refreshed and all CommandLineRunners and ApplicationRunners have been called.
* @since 2.0.0
* 在运行方法彻底完成之前立即调用,刷新ApplicationContext并调用所有CommandLineRunners和ApplicationRunner。
*/
void running(ConfigurableApplicationContext context);
/**
* Called when a failure occurs when running the application.
* @since 2.0.0
* 在运行应用程序时失败时调用。
*/
void failed(ConfigurableApplicationContext context, Throwable exception);
}
后续IOC启动过程中会常出现这些SpringApplicationRunListeners
的身影,我们可以多加留意。
默认情况下加载的listeners有一个,类型为EventPublishingRunListener
。
回到 run 方法中:
//......
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(); //【回调】首次启动run方法时立即调用。可用于非常早期的初始化(准备运行时环境之前)。
try {
// 将main方法的args参数封装到一个对象中
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//......
在 prepareEnvironment 之前,run方法中调用了:listeners.starting() ,已经开始了事件回调。
2.2 prepareEnvironment:准备运行时环境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
// 创建运行时环境
// 根据当前的应用运行环境类型,创建不同的 Environment 。默认 SpringBoot 环境下会创建 StandardServletEnvironment 。
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置运行时环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 【回调】SpringApplicationRunListener的environmentPrepared方法(Environment构建完成,但在创建ApplicationContext之前)
listeners.environmentPrepared(environment);
// 环境与应用绑定
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
2.3 createApplicationContext:创建IOC容器
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 根据Web应用类型决定实例化哪个IOC容器
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);
}
可以发现都是创建的基于Annotation的 ApplicationContext。
注意,BeanFactory 在这里已经被创建了:
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
对三种类型的运行时环境、IOC容器的类型归纳一下:
- Servlet - StandardServletEnvironment - AnnotationConfigServletWebServerApplicationContext
- Reactive - StandardReactiveWebEnvironment - AnnotationConfigReactiveWebServerApplicationContext
- None - StandardEnvironment - AnnotationConfigApplicationContext
2.4 prepareContext:初始化IOC容器
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 将创建好的应用环境设置到IOC容器中
context.setEnvironment(environment);
// IOC容器的后置处理
postProcessApplicationContext(context);
// 执行Initializer
applyInitializers(context);
// 【回调】SpringApplicationRunListeners的contextPrepared方法(在创建和准备ApplicationContext之后,但在加载之前)
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 创建两个组件:在控制台打印Banner的,之前把main方法中参数封装成对象的组件
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]));
// 【回调】SpringApplicationRunListeners的contextLoaded方法(ApplicationContext已加载但在刷新之前)
listeners.contextLoaded(context);
}
postProcessApplicationContext:IOC容器的后置处理
public static final String CONFIGURATION_BEAN_NAME_GENERATOR =
"org.springframework.context.annotation.internalConfigurationBeanNameGenerator";
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
// 注册BeanName生成器
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());
}
}
// 设置类型转换器
if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
它设置了几个组件:
- 如果 beanNameGenerator 不为空,则把它注册到IOC容器中。 BeanNameGenerator 是Bean的name生成器,指定的 CONFIGURATION_BEAN_NAME_GENERATOR 在修改首字母大写后无法从IDEA索引到,暂且放置一边。
- ResourceLoader 和 ClassLoader,这些都在前面准备好了
- ConversionService,用于类型转换的工具,前面也准备好了,并且还做了容器共享
applyInitializers:执行Initializer
这个方法会获取到所有 Initializer,调用initialize
方法。
而这些 Initializer,其实就是刚创建 SpringApplication 时准备的那些 ApplicationContextInitializer。见1.1
加载、注册主启动类 等预处理工作
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
// 设置BeanName生成器,通过Debug发现此时它还没有被注册
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();
}
将启动类包装为BeanDefinition注册到IOC容器中,调用BeanDefinitionReaderUtils的registerBeanDefinition方法。
2.5 refreshContext:刷新容器
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
它直接调了refresh方法(注意此时还是 SpringApplication,没有进到真正的IOC容器),后面又注册了一个关闭的钩子。
作用是监听JVM关闭时销毁IOC容器和里面的Bean。
下面我们来看IOC容器启动时最核心的refresh方法:
这个方法很长,也很重要,分为十三步。
// 最终调到AbstractApplicationContext的refresh方法
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// ...
try {
// Prepare this context for refreshing.
// 1 初始化前的预处理
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 2 获取BeanFactory,加载所有bean的定义信息(未实例化)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 3 BeanFactory的预处理配置
prepareBeanFactory(beanFactory);
// Allows post-processing of the bean factory in context subclasses.
// 4 BeanFactory的后置处理
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 5 执行BeanFactory创建后的后置处理器
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 6 注册Bean的后置处理器
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 7 初始化MessageSource(SpringMVC)
initMessageSource();
// Initialize event multicaster for this context.
// 8 初始化事件派发器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 9 子类的多态onRefresh
onRefresh();
// Check for listener beans and register them.
// 10 注册监听器
registerListeners();
// 到此为止,BeanFactory已创建完成
// Instantiate all remaining (non-lazy-init) singletons.
// 11 初始化剩余的单实例Bean
finishBeanFactoryInitialization(beanFactory);
// 12 完成容器的创建工作
finishRefresh();
}
catch (BeansException ex) {
// ...
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
// 13 清除缓存
resetCommonCaches();
}
}
}
其中主要有三个逻辑,我们慢慢看: