SpringBoot启动源码的个人分析
首先看一下main函数:
@SpringBootApplicationpublic class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
很简单的一个函数,直接调用了 SpringApplication
的 run
方法,查看 run
方法,如下所示:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
调用的 run
调用了自身一个重载方法,这个重载方法调用了 SpringApplication
的构造方法来创建一个 SpringApplication
对象,接着再调用这个 SpringApplication
对象的 run
方法,传递运行的指令参数 args
(本例中并无运行指令参数)。先看下 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));
this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 项目类型判断
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 设置初始化器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 设置监听器
this.mainApplicationClass = deduceMainApplicationClass(); // main方法判断
}
其中传入的参数 resourceLoader
为 null,而 primarySources
为只有一个元素的数组,元素为 DemoApplication.class
。前三行代码不做说明,主要是赋值和非空校验。
第四行代码:this.webApplicationType = WebApplicationType.deduceFromClasspath();
,用来判断当前项目的类型,是 servlet,reactive 还是就是一个简单的项目,通过检查当前jar包是否存在指定的类来进行区分。代码实现简单,就不再展示。
PS:本文的项目为 servlet 项目,所以
webApplicationType
为WebApplicationType.SERVLET
。
第五、第六行代码,分别为初始化器、监听器的设置。两者均通过 getSpringFactoriesInstances
方法获取指定类型的实例集合。getSpringFactoriesInstances
方法获取的是 META-INF/spring.factories
文件中的相关配置,代码如下:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // 获取类加载器,由于之前未传递类加载器,这里获取到的是AppClassLoader
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // SpringFactoriesLoader.loadFactoryNames 方法加载 spring.factories 配置信息
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 通过反射创建相关的实例对象
AnnotationAwareOrderComparator.sort(instances); // 排序
return instances;
}
这里方法实现都很简单,通过方法名也能很清楚的了解它们的作用。第七行代码 this.mainApplicationClass = deduceMainApplicationClass();
用来获取 mian 方法所在的类,通过新建异常获取其栈信息,遍历到第一个有 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
方法才是最主要的实现,先来看下代码:
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);
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, 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;
}
首先看到 ① 处代码的 StopWatch
,其作用就是个计时器,配合 ⑪ 处代码,在日志中打印程序启动耗时,如:
[main] INFO com.loewi_xi.demo.DemoApplication - Started DemoApplication in 2.002 seconds (JVM running for 2.414)
接着往下看 ② 处代码,方法实现很简单,不在贴出来,其主要确认 java.awt.headless
这个环境变量是否存在,不存在的话则设置默认值 true。
当代码执行到 ③ 处的时候,便进入了核心部分了。getRunListeners
方法内部其实只是获取 META-INF/spring.factories
中配置的 org.springframework.boot.SpringApplicationRunListener
相关信息。通过 getRunListeners
获取的监听器,调用其 starting
、started
等方法来告知用户当前程序的状态。
当通过获取到的监听器告诉用户程序正在启动之后,便到了 ④ 处 —— 环境变量和参数的处理。处理完之后,通过 ⑤ 处会在日志中打印相关的logo信息。打印完logo之后便来到关键的 ⑥ 处,创建 ApplicationContext
。createApplicationContext
方法会依赖之前判断项目类型 webApplicationType
的值,来发射生成对应的 ApplicationContext
。
PS:由于
webApplicationType
为WebApplicationType.SERVLET
,所以具体的ApplicationContext
为org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
。
⑦ 处获取 META-INF/spring.factories
中配置的异常报告配置信息,当启动时有异常产生时,通过它以及 ⑬ 处代码的实现,来进行通知处理。
接着看下 ⑧ 处 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);
}
// 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);
}
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);
}
这段代码的要重点关注 load the sources 这段实现,这里会根据 xml 中的配置或者注解配置,把各个 bean 生成 BeanDefinition 并进行注册。注册的实现如下:
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier, @Nullable BeanDefinitionCustomizer[] customizers) { AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
abd.setInstanceSupplier(supplier);
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class == qualifier) {
abd.setPrimary(true);
} else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
} else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
if (customizers != null) {
for (BeanDefinitionCustomizer customizer : customizers) {
customizer.customize(abd);
}
}
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
当 prepareContext
完成之后,便到了 ⑨ 处:refreshContext
,而 refreshContext
的最终实现如下:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) {
// 给当前上下文做好刷新准备
prepareRefresh();
// 通知子类,刷新内部的 BeanFactory,并返回
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 给 BeanFactory 初始化相关信息,以便当前上下文能正常使用
prepareBeanFactory(beanFactory);
try {
// 在上一步处理的基础上,让各个子类进行各自自定义的处理
postProcessBeanFactory(beanFactory);
// 把所有的 BeanFactoryPostProcessor 作为 Bean 注册到上下文中
invokeBeanFactoryPostProcessors(beanFactory);
// 注册处理 Bean 创建相关的 Processor
registerBeanPostProcessors(beanFactory);
// 初始化当前上下文的消息信息(国际化)
initMessageSource();
// 初始化当前上下问的消息传播器
initApplicationEventMulticaster();
// 在上面初始化的基础上,让各个子类进行各自自定义的初始化
onRefresh();
// 校验监听器,并注册到当前上下文中
registerListeners();
// 初始化所有非懒加载的单例 Bean
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;
}
}
}
当完成 refreshContext
之后,便来到了 ⑩ 处,该方法暂时未有任何实现,预留用于 refresh 完成之后的后续处理。最后执行到 ⑫ 处。这里的 callRunners
方法,其实就是调用程序中 ApplicationRunner
或 CommandLineRunner
相关接口的实现,即希望程序启动时需要执行的操作。
以上只是个人的看法,如有问题处,欢迎指出,我会积极改正。以上基于 Spring Boot 2.2.4.RELEASE。
以上是 SpringBoot启动源码的个人分析 的全部内容, 来源链接: utcz.com/z/513981.html