写在前面
因为本人也是先看了一些相应的Spring相关的源码阅读的视频和文章,但是还是对这部分一知半解,并且比较懒惰,所以以记录的方式来督促自己整体阅读下来,肯定会有很多遗漏的地方,请见谅。
个人最终的目的就是把注释覆盖到所有启动流程的每一行代码中,如果各位觉得哪一处有冗余的地方,请在评论中指出。
整体阅读
因为本人也是第一次真正意义上的自己上手阅读Spring相关源码,所以可能会有一个粗略阅读的部分,这部分也会进行相应的记录,如果已经有了大概的脉络则跳过整体阅读这个章节,那么马上开始。
环境搭建
首先先从启动类入手,我们先搭建一个最简单的Spring Boot环境,这次的阅读的源码版本是2.6.0-SNAPSHOT。
4.0.0
com.zhisan
spring-boot-study
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-parent
2.6.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-web
spring-snapshots
https://repo.spring.io/snapshot
true
spring-milestones
https://repo.spring.io/milestone
spring-snapshots
https://repo.spring.io/snapshot
spring-milestones
https://repo.spring.io/milestone
package com.zhisan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AppRun {
public static void main(String[] args) {
SpringApplication.run(AppRun.class, args);
}
}
启动类入手
然后启动我们的AppRun,相信很多人都是从@SpringBootApplication入手的,的确如果应对面试的话,那你只需要解释这部分的内容就可以秒杀大部分的小公司了,但是我们这次是完全的源码解析,所以我们的的重点还是放在如下几个方面。
- 如何把他运行起来
- 运行起来后是如何获取bean等相关配置的
这是我们这次粗略整体阅读的最简单的目的。
首先我们把几个重要的部分都抽离开来。
- SpringApplication(类初始化)
- SpringApplication.run(方法调用)
启动类
org.springframework.boot.SpringApplication
首先定位到这个构造方法,看看他为我们准备哪些东西。
public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {
// 资源加载器=null
this.resourceLoader = resourceLoader;
// 判断主要资源是否为空,为空则断言抛出异常IllegalArgumentException
Assert.notNull(primarySources, "PrimarySources must not be null");
// 主要资源去重后放到primarySources中
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 根据路径推断类路径,最终得出结果“SERVLET”
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 从 Spring 工厂获取 Bootstrap Registry Initializers
this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
// 获取 Spring 工厂实例 -> 应用程序上下文初始化程序
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 获取 Spring 工厂实例 -> 应用程序监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推导出主应用程序类
this.mainApplicationClass = deduceMainApplicationClass();
}
简单的阅读过后,我们得出一个结论,这部分就是一些基本类信息的获取和设置,类似于环境准备的功能,那么我们最终得到的几个资源就是。
- 主要资源列表
- Web App类型Servlet
应用程序上下文初始化程序
- org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
应用程序监听器
- 0 = "org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor"
- 1 = "org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor"
- 2 = "org.springframework.boot.env.RandomValuePropertySourceEnvironmentPostProcessor"
- 3 = "org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor"
- 4 = "org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor"
- 5 = "org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor"
- 6 ="org.springframework.boot.autoconfigure.integration.IntegrationPropertiesEnvironmentPostProcessor"
- 主应用程序完整类名com.zhisan.AppRun
其中应用程序监听器中被加载的部分是我们比较关注的,因为我们暂时还不知道这些内容是怎么被加载进来的,可以参考加载Spring工厂章节。
启动类启动方法
org.springframework.boot.SpringApplication#run(java.lang.String...)
初始化完SpringApplication就可以运行他的run方法了,大部分的整体运行逻辑也都在此处,我们这部分重点解析。
public ConfigurableApplicationContext run(String... args) {
// 启动一个简单的秒表
// tip:用于计时,可以忽略
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 创建引导上下文
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// 定义可配置的应用程序上下文变量
ConfigurableApplicationContext context = null;
// 配置无头属性
configureHeadlessProperty();
// 获取运行监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 启动监听器
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 默认应用程序参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 配置忽略 Bean 信息
configureIgnoreBeanInfo(environment);
// 打印横幅 Spring图标(可自定义)
Banner printedBanner = printBanner(environment);
// 创建应用程序上下文
context = createApplicationContext();
// 设置应用程序启动
context.setApplicationStartup(this.applicationStartup);
// 准备上下文
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 刷新上下文
// 这里会涉及启动Spring
// 启动Web服务
refreshContext(context);
// 刷新后执行
// tip:未实现为了扩展性
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, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
逐行阅读
从类路径推断Web类型
org.springframework.boot.WebApplicationType#deduceFromClasspath
static WebApplicationType deduceFromClasspath() {
// 判断org.springframework.web.reactive.DispatcherHandler存在,并且org.springframework.web.servlet.DispatcherServlet和org.glassfish.jersey.servlet.ServletContainer不存在
// 则采用reactive方式
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// 存在"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"其中一个则返回NONE
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
// 如果都匹配不到则采用默认Servlet
return WebApplicationType.SERVLET;
}
从 Spring 工厂获取 Bootstrap Registry Initializers
org.springframework.boot.SpringApplication#getBootstrapRegistryInitializersFromSpringFactories
private List getBootstrapRegistryInitializersFromSpringFactories() {
// 初始化列表
ArrayList initializers = new ArrayList<>();
getSpringFactoriesInstances(Bootstrapper.class).stream()
.map((bootstrapper) -> ((BootstrapRegistryInitializer) bootstrapper::initialize))
.forEach(initializers::add);
// 引导注册表初始化程序
initializers.addAll(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
return initializers;
}
加载Spring工厂
org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories
框架内内部使用的通用工厂加载机制。
SpringFactoriesLoader从“META-INF/spring.factories”文件加载和实例化给定类型的工厂,这些文件可能存在于类路径中的多个 JAR 文件中。 spring.factories文件必须是Properties格式,其中 key 是接口或抽象类的完全限定名称,value 是逗号分隔的实现类名称列表。 例如:
example.MyService=example.MyServiceImpl1,example.MyServiceImpl2
其中example.MyService是接口名称, MyServiceImpl1和MyServiceImpl2是两个实现。
上面是org.springframework.core.io.support.SpringFactoriesLoader的类介绍,如果看完这部分介绍,已经有点眉目了,那么我下面就是这个类中具体干活的一个方法的逐行解析。
private static Map> loadSpringFactories(ClassLoader classLoader) {
// 获取相应类加载器中内容
Map> result = cache.get(classLoader);
// 存在则返回类加载器中内容
if (result != null) {
return result;
}
// 不存在则初始化类加载器中内容
result = new HashMap<>();
try {
// 获取资源 -> META-INF/spring.factories 列表
// 可能存在多个META-INF/spring.factories 文件
Enumeration urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
// 循环加载
while (urls.hasMoreElements()) {
// 获取 META-INF/spring.factories 文件URL地址
URL url = urls.nextElement();
// 加载资源
UrlResource resource = new UrlResource(url);
// 加载资源中配置
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
// 循环配置 类似properties key:value形式
for (Map.Entry, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
// 逗号分隔列表到字符串数组
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
// 循环value中子项到列表中
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// 列表去重
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
// 保存该列表
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
具体解析一下这部分代码,从缓存(cache)中根据不同的类加载器获取其中的键值对,如果存在则返回类加载器中的内容,不重复读取。如果没找到键值对,则获取类加载器中能获取到的META-INF/spring.factories文件中的内容,逐一解析最后放到缓存中,等待下次获取。
这部分方法可以看出,SpringBoot设置了一些默认的接口类和具体实现类到配置文件中,类似于以前经常用到的properties文件的方式。
例如:
example.MyService=example.MyServiceImpl1,example.MyServiceImpl2
前者是接口类,后者是该接口的具体实现方法。
获取 Spring 工厂实例
org.springframework.boot.SpringApplication#getSpringFactoriesInstances(java.lang.Class, java.lang.Class>[], java.lang.Object...)
private Collection getSpringFactoriesInstances(Class type, Class>[] parameterTypes, Object... args) {
// 获取类加载器
ClassLoader classLoader = getClassLoader();
// 使用名称并确保唯一以防止重复
// Use names and ensure unique to protect against duplicates
Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 创建 Spring 工厂实例
List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
获取类加载器
org.springframework.boot.SpringApplication#getClassLoader
public ClassLoader getClassLoader() {
if (this.resourceLoader != null) {
return this.resourceLoader.getClassLoader();
}
return ClassUtils.getDefaultClassLoader();
}
获取 Spring 工厂实例
org.springframework.boot.SpringApplication#createSpringFactoriesInstances
private List createSpringFactoriesInstances(Class type, Class>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set names) {
// 创建实例列表
List 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;
}
创建引导上下文
org.springframework.boot.SpringApplication#createBootstrapContext
private DefaultBootstrapContext createBootstrapContext() {
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
return bootstrapContext;
}
实例化类
org.springframework.beans.BeanUtils#instantiateClass(java.lang.reflect.Constructor, java.lang.Object...)
public static T instantiateClass(Constructor ctor, Object... args) throws BeanInstantiationException {
Assert.notNull(ctor, "Constructor must not be null");
try {
ReflectionUtils.makeAccessible(ctor);
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass())) {
return KotlinDelegate.instantiateClass(ctor, args);
}
else {
Class>[] parameterTypes = ctor.getParameterTypes();
Assert.isTrue(args.length <= parameterTypes.length, "Can't specify more arguments than constructor parameters");
Object[] argsWithDefaultValues = new Object[args.length];
for (int i = 0 ; i < args.length; i++) {
if (args[i] == null) {
Class> parameterType = parameterTypes[i];
argsWithDefaultValues[i] = (parameterType.isPrimitive() ? DEFAULT_TYPE_VALUES.get(parameterType) : null);
}
else {
argsWithDefaultValues[i] = args[i];
}
}
return ctor.newInstance(argsWithDefaultValues);
}
}
catch (InstantiationException ex) {
throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
}
catch (IllegalArgumentException ex) {
throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
}
catch (InvocationTargetException ex) {
throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
}
}
这部分的因为涉及Spring源码太多,最主要的就是
ctor.newInstance(argsWithDefaultValues)
这个方法,用构造器实例化对象。
Spring刷新
org.springframework.context.support.AbstractApplicationContext#refresh
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// 准备刷新
// tip:一些设置参数,可不细看
prepareRefresh();
// 告诉子类刷新内部 bean 工厂
// 获取刷新 bean 工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 准备 bean 工厂
prepareBeanFactory(beanFactory);
try {
// 允许在上下文子类中对 bean 工厂进行后处理。
// tip:这部分涉及Web服务器的启动,如servlet
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// 调用在上下文中注册为 bean 的工厂处理器。
invokeBeanFactoryPostProcessors(beanFactory);
// 注册拦截 bean 创建的 bean 处理器。
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// 初始化此上下文的消息源。
initMessageSource();
// 为此上下文初始化事件多播器。
initApplicationEventMulticaster();
// 初始化特定上下文子类中的其他特殊 bean。
onRefresh();
// 检查侦听器 bean 并注册它们。
registerListeners();
// 实例化所有剩余的(非延迟初始化)单例。
finishBeanFactoryInitialization(beanFactory);
// 最后一步:发布相应的事件。
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();
contextRefresh.end();
}
}
}
这部分不过多阅读,涉及Spring部分太深。
参考资料
java.awt.headless 模式
全网最权威的Spring+Spring Boot源码解析!阿里资深架构师用450分钟让你掌握Spring和Spring Boot。
文章中出现的任何错误欢迎指正!共同进步!
最后做个小小广告,有对Java开发感兴趣的,可以加群一起学习和交流!
QQ:425343603