SpringBoot启动源码的个人分析

编程

首先看一下main函数:

@SpringBootApplication

public class DemoApplication {

public static void main(String[] args) {

SpringApplication.run(DemoApplication.class, args);

}

}

很简单的一个函数,直接调用了 SpringApplicationrun 方法,查看 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 项目,所以 webApplicationTypeWebApplicationType.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 获取的监听器,调用其 startingstarted 等方法来告知用户当前程序的状态。

当通过获取到的监听器告诉用户程序正在启动之后,便到了 ④ 处 —— 环境变量和参数的处理。处理完之后,通过 ⑤ 处会在日志中打印相关的logo信息。打印完logo之后便来到关键的 ⑥ 处,创建 ApplicationContextcreateApplicationContext 方法会依赖之前判断项目类型 webApplicationType 的值,来发射生成对应的 ApplicationContext

PS:由于 webApplicationTypeWebApplicationType.SERVLET,所以具体的 ApplicationContextorg.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 方法,其实就是调用程序中 ApplicationRunnerCommandLineRunner 相关接口的实现,即希望程序启动时需要执行的操作。

以上只是个人的看法,如有问题处,欢迎指出,我会积极改正。以上基于 Spring Boot 2.2.4.RELEASE。

以上是 SpringBoot启动源码的个人分析 的全部内容, 来源链接: utcz.com/z/513981.html

回到顶部