基于Springboot3.2.1, JDK17
个人总结,如有误区,请指正。
初衷是为了能更全面地理解,增强在项目中的实际应用,以及提升后期排错能力。
创建一个demo Spring boot:https://start.spring.io/
Q1: Spring boot是什么?
是一个企业级Java应用开发轻量级框架。基于Spring强大的生态,通过约定大于配置的思路,简化开发难度(通过IOC,AOP,以及注解的方式),让开发人员更专注于对业务的实现。
Q2: 简要地说明Springboot如何运作?
-
基于IOC容器运作流程。
- BeanDefinition
- BeanDefintionRegistry
- BeanDefinitionReader
- BeanFactoryPostProcessor
- BeanFactory
- ApplicationContext
- BeanPostProcessor
- Bean
-
先定义bean源信息(途径:xml配置,annonation注解配置,或其他)
-
将bean信息通过
BeanDefinitionReader
读取,加载成规范的BeanDefinition
-
在真正
BeanDefinition
被BeanFactory
拿去做初始化之前,Spring会通过BeanFactoryPostProcessor
后置处理器作为扩展功能,比如:org.springframework.context.support.PropertySourcesPlaceholderConfigurer
:<context:property-placeholder>
在xml中动态定义db参数的扩展,实现可以通过${}
方式,在指定的classpth properties
文件中获取。 -
BeanFactory
开始拿到BeanDefinition
,开始初始化:根据beanClassName
, 通过反射机制实现初始化对象。这个过程涉及到一个热点问题:Spring的循环依赖:三级缓存。后续再说。 -
在实例化之前,Spring定义了另外一个扩展:
BeanPostProcessor
。这一个类定义了两个方法:#postProcessBeforeInitialization
& #postProcessAfterInitialization
。顾名思义,会在实例化的前后对Bean对象进行扩展修饰。 -
实例化Bean对象。会对成员变量进行populate初始值。
Q3: Springboot如何实现自动装配?
- 基于Q2的理解,可以得到两个信息,如果我想自定义做一些扩展功能。可以从PostProcessor这个后置处理器上下功夫。
- 针对自动装配,是通过pom加入依赖,主类加注解
@SpringbootApplication
,以及main方法的编写实现的。 - 此外,可以确定的是,只要确保在
BeanFactory
在初始化Bean对象的时候,能给到正确规范的BeanDefinition
定义对象即可。不论bean源信息是如何实现的。 - 所以,针对bean源信息的定义:
注解,xml,javaconfig,抑或是自定义的json等等形式
,只要确保定制专门的reader以及beanFactoryPostProcessor能够最终得到正确规范的beanDefinition。Springboot的自动装配的确也是这么做的: - 在代码层面上,在#
refresh
方法中的#finishBeanFactoryInitialization
方法(beanFactory完成对Bean对象的初始化,实例化过程)之前,我可以做自动装配这个动作:(这里列举了详细过程,结尾有总结)- 在#main方法启动时,
-
首先
SpringApplication
类会被实例化,在此期间,有两个主要的点:cache
会被load
所有的META-INF/spring.factories
下的resource- 设置主类
primarySource
BTW:Springboot源码中,在给map赋值时,使用#
computeIfAbsent
方法(如果key对应的value为null,那么执行lambda表达式,返回的值,赋值给key对应的value值)频次极高。
-
- 在#main方法启动时,
public static SpringFactoriesLoader forResourceLocation(String resourceLocation, @Nullable ClassLoader classLoader) {
Assert.hasText(resourceLocation, "'resourceLocation' must not be empty");
ClassLoader resourceClassLoader = classLoader != null ? classLoader : SpringFactoriesLoader.class.getClassLoader();
// 第一次cache是个size=0的map集合
// computeIfAbsent后,会将 resourceClassLoader::loader存进cache的集合中
Map<String, SpringFactoriesLoader> loaders = (Map)cache.computeIfAbsent(resourceClassLoader, (key) -> {
return new ConcurrentReferenceHashMap();
});
// 这里会对loader进行存值,值就是来自于META/INF/spring.factories
return (SpringFactoriesLoader)loaders.computeIfAbsent(resourceLocation, (key) -> {
return new SpringFactoriesLoader(classLoader, loadFactoriesResource(resourceClassLoader, resourceLocation));
});
}
- 执行#run方法过程:
-
当执行到#
prepareContext
时,会去加载主类:从primarySource拿到主类,通过创建BeanDefintionLoader
加载器,使用AnnonatedBeanDefinitionReader
将主类包装成AnnotatedGenericBeanDefinition
注册到beanFactory
(BeanDefinitionRegistry
)中(主类被加到beanDefinitionMap
中)。这一步主要是为了后面执行ConfigurationClassPostProcessor
(这是一个BeanFactoryPostProcessor
)调用时,找到主类,完成对注解的剖析过程。 -
随后执行#
refreshContext
方法:- 方法#
invokeBeanFactoryPostProcessor
,会去从BeanFactory
中拿取类型为BeanDefinitionRegistryPostProcessor
(这继承了BeanFactoryPostProcessor
),随后获得这次的主角:ConfigurationClassPostProcessor
。- 程序会调用到
ConfigurationClassPostProcessor
的#processConfigBeanDefinitions
方法:- 先从
beanDefinitionNames
中拿到主类,且包装成BeanDefinitionHolder
; - 随后,会定义
ConfigurationClassParser
类,在方法#processConfigurationClass方法中,递归处理Configuration class及其super class:- 在方法#
processImports
中的getImports
, 通过主类注解@SpringBootApplication
,递归调用,找到包括父注解中,包含@Import
注解上的class类(@EnableAutoConfiguration
注解上的AutoConfigurationImportSelector
类,@AutoConfigurationPackage
注解上的AutoConfigurationPackages.Registrar
类)。
- 在方法#
- 先从
- 随后,再通过
SelectorHandler
的#processGroupImports
方法,调用到AutoConfigurationImportSelector
的#process
– #getImports – #getAutoConfigurationEntry
– #getCandidateConfigurations
获得在路径META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
下所有类对象。 - 拿到所有**AutoConfiguration类后,跟主类同样地操作,去遍历处理各个类的注解信息,找到
@Import
下的类,并对其进行解析。 - 查找完之后,我本地最终剩余54个config class。
- 通过创建
ConfigurationClassBeanDefinitionReader
,#loadBeanDefinitions
方法读取加载这些Config class。 - 最终会将加载好的config class以及config class下的加有
@Bean
的method方法以形式<beanName, BeanDefinition>
的数据方式放在BeanFactory
下的beanDefinitionMap
中。 - 然后,在这个时机,才去真正遍历调用
BeanFactoryPostProcessor
的post方法。 - 在
invokeBeanFactoryPostProcessors
方法调用完成后,查看到beanFactory
的BeanDefinitionNames
属性,会发现,之前加载的config class
以及class下的method bean
都在这个列表里面。 - 在实例化Bean时,config class会被加载,完成对Springboot的启动配置。
- 程序会调用到
- 方法#
-
总而言之,springboot自动装配是通过ConfigurationClassPostProcessor
主要完成了对注解的解析(ConfigurationClassParser
),对所有config class加载读取(ConfigurationClassBeanDefinitionReader
)两项主要工作。
-
解析工作:通过遍历主类注解,查找出@Import的导入类,通过该类从
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
下查找到所有的config class,也会对查找的config class进行同样的遍历查找,找出所有的配置类。 -
加载读取工作:会对找出的所有配置类,配置类中的Method Bean, 配置类的importedResources以及配置类的ImportBeanDefinitionRegistrars进行读取,放置到
BeanFactory
的BeanDefinitionMap
中,等待随后的bean初始化,实例化过程,完成自动配置启动。