1、SpringApplication的构造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//设置servlet环境,分为非web环境、web环境、reactive环境三种
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//加载META-INF/spring.factories文件中的ApplicationContextInitializer和ApplicationListener
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//推断main方法所在的类
this.mainApplicationClass = deduceMainApplicationClass();
}
这里主要是通过判断REACTIVE相关的字节码是否存在,如果不存在,则web环境即为SERVLET类型。这里设置好web环境类型,在后面会根据类型初始化对应环境。
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
deduceMainApplicationClass的实现原理比较巧妙,这里新建了一个运行时异常对象,通过这个对象获取当前的调用函数堆栈数组StackTrace,之后遍历这个堆栈数组,找到方法名为main的类,返回这个类。
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
SpringApplication类初始化完成之后执行run方法开始启动流程,启动流程分为七个步骤:
- 第一步:获取并启动监听器
- 第二步:构造容器环境
- 第三步:创建容器
- 第四步:实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
- 第五步:准备容器
- 第六步:刷新容器
- 第七步:刷新容器后的扩展接口
public ConfigurableApplicationContext run(String... args) {
//时间监控
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//java.awt.headless是J2SE的一种模式用于在缺少显示屏、键盘或者鼠标时的系统配置,很多监控工具如jconsole 需要将该值设置为true,系统变量默认为true
configureHeadlessProperty();
//获取spring.factories中的监听器变量,args为指定的参数数组,默认为当前类SpringApplication
//第一步:获取并启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//第二步:构造容器环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//设置需要忽略的bean
configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = printBanner(environment);
//第三步:创建容器
context = createApplicationContext();
//第四步:实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
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, 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;
}
一、获取并启动监听器
1、获取监听器
获取监听器的源码核心是下面两个方法,这里是先加载SpringApplicationRunListener,EventPublishingRunListener实现了SpringApplicationRunListener接口,通过反射生成实例时,会触发EventPublishingRunListener的构造方法,构造方法会将spring.factories中的监听器(SpringApplication的构造方法已经将其加载到内存中)传递到SimpleApplicationEventMulticaster中,后续启动监听器,将从这里获取到这些监听器。 除此之外,下面两个方法还用来加载META-INF/spring.factories文件中的ApplicationContextInitializer和ApplicationListener。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
/**
* 整个 springBoot 框架中获取factories的方式
*/
@SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
//装载class文件到内存
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
//主要通过反射创建实例
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
//EventPublishingRunListener的构造方法
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
2、启动监听器
getRunListeners(args)方法执行时调用了SpringApplicationRunListeners的构造方法,所以starting()方法里的listeners只有EventPublishingRunListener对象
void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
EventPublishingRunListener对象的starting()方法会发布事件类型为ApplicationStartingEvent的事件,这里主要是使用spring的事件机制实现事件传递。
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
下面的代码时事件启动的核心,会先根据事件类型获取到预先加载到AbstractApplicationEventMulticaster的监听器,这里获取到的是事件类型为ApplicationStartingEvent的事件,在运行的不同阶段会执行不同的监听器。
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
//获取线程池,如果为空则同步处理。这里线程池为空,还未没初始化。
Executor executor = this.getTaskExecutor();
//根据事件类型获取监听器
Iterator var5 = this.getApplicationListeners(event, type).iterator();
while(var5.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var5.next();
if (executor != null) {
//异步发布事件
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
//同步发布事件
this.invokeListener(listener, event);
}
}
}
二、构造容器环境
构造容器环境的时候首先会得到一个ConfigurableEnvironment对象,getOrCreateEnvironment()方法会根据运行环境返回不同的environment对象,Environment接口提供了4种实现方式,StandardEnvironment、StandardServletEnvironment和MockEnvironment、StandardReactiveWebEnvironment,分别代表普通程序、Web程序、测试程序的环境、响应式web环境,当new一个environment对象时,会完成一些列初始化工作获取到系统变量和环境变量,并将运行机器的系统变量和环境变量,加入到其父类AbstractEnvironment定义的对象MutablePropertySources中。 系统环境初始化完成之后会发布一个事件,与启动监听器类似,完成对项目中配置文件的解析。这里会获取到事件类型为ConfigurableEnvironment的监听器,最为核心的一个监听器是ConfigFileApplicationListener,他主要用来 properties 和yml文件。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
// 创建ConfigurableEnvironment对象,并且获取运行环境(servlet或reactive或非web环境)的初始化参数和上下文参数,系统变量和环境变量参数
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置动作,将运行机器的系统变量和环境变量,加入到其父类AbstractEnvironment定义的对象MutablePropertySources中
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
// 发布系统环境初始化完成的事件,解析项目中的配置文件
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
ConfigFileApplicationListener的onApplicationEnvironmentPreparedEvent会先获取到spring.factories中的EnvironmentPostProcessor环境配置处理器,ConfigFileApplicationListener会先执行所有的EnvironmentPostProcessor,最后执行自生的代码逻辑,利用其内部类Loader加载指定目录下的配置文件。
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
三、创建容器
创建容器的逻辑非常的简单,他是根据webApplicationType判断,创建与普通web环境,webflux环境和非web环境对应的容器,同样是使用的反射来创建对象实例。
/**
* The class name of application context that will be used by default for web
* environments.
*/
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
/**
* The class name of application context that will be used by default for reactive web
* environments.
*/
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 {
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);
}
四、报告错误信息
通过源码可以看出,先调用了getSpringFactoriesInstances()方法获取到spring.factories中的FailureAnalysisReporter,当在启动过程中捕捉到异常时,执行handleRunFailure()方法,
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
try {
try {
//处理程序退出码
handleExitCode(context, exception);
if (listeners != null) {
//执行监听器的失败方法
listeners.failed(context, exception);
}
}
finally {
//上报打印错误信息
reportFailure(exceptionReporters, exception);
if (context != null) {
context.close();
}
}
}
catch (Exception ex) {
logger.warn("Unable to close ApplicationContext", ex);
}
ReflectionUtils.rethrowRuntimeException(exception);
}
五、容器准备
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//设置容器环境,包含各种参数和变量
context.setEnvironment(environment);
//容器的后置处理,因为beanNameGenerator和resourceLoader默认为空,所以这个方法只会执行数据类型转化器的注册
postProcessApplicationContext(context);
//执行初始化处理器(spring.factories中定义的Application Context Initializers)
applyInitializers(context);
//发送容器已经准备好的事件,通知到各种监听器,这里会按照事件类型为ApplicationContextInitializedEvent过滤监听器
listeners.contextPrepared(context);
//打印日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
//加载Spring Boot中特殊的两个单例类springApplicationArguments和springBootBanner
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
//设置beanFactory中是否允许重复bean覆盖
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
//设置是否使用延迟初始化,默认不使用,非必要情况下不建议使用,正常初始化即可,延迟初始化不一定优化性能,在多线程情况下,反而可能会带来更大的开销
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// 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);
}
六、刷新容器
Spring Boot在做容器刷新的时候其实做了两件事情,首先是刷新容器,而且刷新容器的主要逻辑都是由Spring完成的,这也是Spring实现IOC和AOP的关键,完成容器刷新之后,会注册一个程序关闭使用的钩子函数。
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
/**
* 这个狗子函数的本质是创建了一个线程,用于在系统退出时关闭资源,平滑退出,以下情况会触发钩子函数:
* 程序正常退出
* 使用System.exit()
* 终端使用Ctrl+C触发的中断
* 系统关闭
* OutofMemory宕机
* 使用Kill pid杀死进程(使用kill -9是不会被调用的)
*/
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.
/**
* 刷新上下文环境
* 初始化上下文环境,对系统的环境变量或者系统属性进行准备和校验
* 如环境变量中必须设置某个值才能运行,否则不能运行,这个时候可以在这里加这个校验,
* 重写initPropertySources方法就好了
*/
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//初始化BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
/**
* 为上下文准备BeanFactory,即对BeanFactory的各种功能进行填充,如常用的注解@Autowired @Qualifier等
* 设置SPEL表达式#{key}的解析器
* 设置资源编辑注册器,如PerpertyEditorSupper的支持
* 添加ApplicationContextAwareProcessor处理器
* 在依赖注入忽略实现*Aware的接口,如EnvironmentAware、ApplicationEventPublisherAware等
* 注册依赖,如一个bean的属性中含有ApplicationEventPublisher(beanFactory),则会将beanFactory的实例注入进去
*/
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
//提供子类覆盖的额外处理,即子类处理自定义的BeanFactoryPostProcess
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
/**
* 激活各种BeanFactory处理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor
* 执行对应的postProcessBeanDefinitionRegistry方法 和 postProcessBeanFactory方法
*/
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
/**
* 注册拦截Bean创建的Bean处理器,即注册BeanPostProcessor,不是BeanFactoryPostProcessor,注意两者的区别
* 注意,这里仅仅是注册,并不会执行对应的方法,将在bean的实例化时执行对应的方法
*/
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
//初始化上下文中的资源文件
initMessageSource();
// Initialize event multicaster for this context.
//初始化上下文事件广播器,并放入applicatioEventMulticaster
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
//添加实现了ApplicationListener的监听器的bean实例
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
/**
* 设置转换器
* 注册一个默认的属性值解析器
* 冻结所有的bean定义,说明注册的bean定义将不能被修改或进一步的处理
* 初始化剩余的非延迟加载的单例bean
*/
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
/**
* 初始化生命周期处理器DefaultLifecycleProcessor,DefaultLifecycleProcessor含有start方法和stop方法,spring启动的时候调用start方法开始生命周期,
* spring关闭的时候调用stop方法来结束生命周期,通常用来配置后台程序,启动有一直运行,如一直轮询kafka
* 启动所有实现了Lifecycle接口的类
* 通过spring的事件发布机制发布ContextRefreshedEvent事件,以保证对应的监听器做进一步的处理,即对那种在spring启动后需要处理的一些类,这些类实现了
* ApplicationListener<ContextRefreshedEvent> ,这里就是要触发这些类的执行(执行onApplicationEvent方法)另外,spring的内置Event有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent
* 完成初始化,通知生命周期处理器lifeCycleProcessor刷新过程,同时发出ContextRefreshEvent通知其他人
*/
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();
}
}
}
七、刷新容器后的扩展接口
容器刷新完成之后的后置处理,默认是没有任何操作的,主要用于功能的扩展,可重写该方法自定义实现一些启动完成之后的操作。
/**
* Called after the context has been refreshed.
* @param context the application context
* @param args the application arguments
*/
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}