Spring源码核心篇整体栏目
内容 | 链接地址 |
---|---|
【一】Spring的bean的生命周期 | https://zhenghuisheng.blog.csdn.net/article/details/143441012 |
【二】深入理解spring的依赖注入和属性填充 | https://zhenghuisheng.blog.csdn.net/article/details/143854482 |
【三】精通spring的aop的底层原理和源码实现 | https://zhenghuisheng.blog.csdn.net/article/details/144012934 |
【四】spring中refresh刷新机制的流程和实现 | https://zhenghuisheng.blog.csdn.net/article/details/144118337 |
【五】spring中循环依赖的解决和底层实现 | https://zhenghuisheng.blog.csdn.net/article/details/144132213 |
【六】spring中事务的底层实现与执行流程 | https://zhenghuisheng.blog.csdn.net/article/details/144178500 |
【七】spring中事物传播机制的流程和原理 | https://zhenghuisheng.blog.csdn.net/article/details/144178500 |
【八】spring中配置类底层原理和源码实现 | https://zhenghuisheng.blog.csdn.net/article/details/148657333 |
spring配置类实现原理
- [一. 深入理解spring配置类实现原理](#一. 深入理解spring配置类实现原理)
-
- 1,配置类方法入口checkConfigurationClassCandidate
- 2,解析配置类doProcessConfigurationClass
-
- [2.1. @PropertySource 解析](#2.1. @PropertySource 解析)
- [2.2. @ComponentScan 解析](#2.2. @ComponentScan 解析)
- [2.3. @Import 解析](#2.3. @Import 解析)
- [2.4. @ ImportResource 解析](#2.4. @ ImportResource 解析)
- [2.5. @Bean 解析](#2.5. @Bean 解析)
- [2.6. @父类解析](#2.6. @父类解析)
- 3,注册配置类的beanDefinition
-
- [3.1. @Import注解生成BeanDefinition](#3.1. @Import注解生成BeanDefinition)
- [3.2. @Bean注解生成BeanDefinition](#3.2. @Bean注解生成BeanDefinition)
- [3.3. @ImportResources注解生成BeanDefinition](#3.3. @ImportResources注解生成BeanDefinition)
如需转载,请附上链接:https://blog.csdn.net/zhenghuishengq/article/details/148657333
一. 深入理解spring配置类实现原理
在spring中,内部存在一些配置类,配置注解,在我们使用这些注解的时候,可以手动的将一些配置类加载到spring容器中,类似与比较常规的 @Configuration 注解等,除了这个注解之外,spring内部也提供了多个这种注解,接下来从spring源码中,来分析这些配置类以及配置注解
在spring源码中,会解析配置类,校验是否配置类的入口如下,通过下面这个方法来判断该
java
ConfigurationClassUtils.checkConfigurationClassCandidate(...)
1,配置类方法入口checkConfigurationClassCandidate
其方法详情如下,主要是通过获取类上面的注解,然后通过注解来判断该类是不是配置类

首先会判断这个类上面是不是加了这个 @Configuration
注解
java
AnnotationMetadata metadata;
// 校验是不是加了 @Configuration 注解
if (isFullConfigurationCandidate(metadata)) {
//如果加了,那么给这个注解上面加一个标记
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
return metadata.isAnnotated(Configuration.class.getName());
}
接下来根据源码继续往下走,上面是一个full重的配置类,接下来会判断一些lite配置类,即一些比较轻的配置类
java
else if (isLiteConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
这个lite轻的配置类的具体实现如下,首先会判断要加载的类上面的注解包不包括在 candidateIndicators 集合列表中, 这个set集合中包括的主要注解如下:Component、ComponentScan、Import、ImportResource 这四个,除了这几个之外,下面还会判断内部是否有 @Bean 注解

java
private static final Set<String> candidateIndicators = new HashSet<>(8);
static {
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}
也就是说,只要类上面有 以下注解,那么spring就会将这些类扫描并解析成一个配置类
- @Configuration
- @Bean
- @Component
- @ComponentScan
- @Import
- @ImportResource
2,解析配置类doProcessConfigurationClass
在refresh方法中,会有一个 invokeBeanFactoryPostProcessors 方法,在内部就会调用到这个 doProcessConfigurationClass 真正的去解析这个配置类的方法。接下来直接看源码,看看内部到底是如何解析的。

2.1. @PropertySource 解析
首先第一步是解析带有这个 PropertySource 注解的实体类,这个注解就是用于处理 Properties 配置文件的,将配置文件中的内容加载到spring容器内部
java
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
}
2.2. @ComponentScan 解析
接下来解析的是这个 ComponentScan 注解,ComponentScan说白了就是对 @Component注解的扫描,判断类上面是否有这个 @Component注解,在前面讲解bean的生命周期的时候重点的讲解过。
java
for (AnnotationAttributes componentScan : componentScans) {
//把我们扫描出来的类变为bean定义的集合 真正的解析
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
//循环处理我们包扫描出来的bean定义
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
//判断当前扫描出来的bean定义是不是一个配置类,若是的话 直接进行递归解析
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
//递归解析 因为@Component算是lite配置类
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
首先会将这些扫描到的类解析成一个 BeanDefinition,判断是否存在includeFilters中,excludeFilters中,然后判断是否@Lazy @DependsOn等注解,最后将这些属性全部注册到beanDefinition中返回
java
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName())
最后又会判断这个类属不属于配置类,属于的话再次递归解析,判断这个类可不可能存在父类也需要解析成配置类等,直到返回null跳出递归
java
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
//递归解析 因为@Component算是lite配置类
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
2.3. @Import 解析
接下来就是处理Import的注解,这个部分的解析相对绕一些。首先会调用这个 getImports 方法获取全部带有Import的类
java
processImports(configClass, sourceClass, getImports(sourceClass), true);
然后遍历这些获取的Import实例,里面会有三个分支,校验这些加了Import的类上面是否是单纯的加了这个注解,还是同时实现了别的接口,要对相关实现的接口做不同的判断
java
for (SourceClass candidate : importCandidates) {
...
}
- 1,首先是先看只有单纯加了Import的配置类,spring会直接将这种配置类进行解析注册
java
else {
// 当做配置类再解析,注意这里会标记:importedBy, 表示这是Import的配置的类
// 再执行之前的processConfigurationClass()方法 ,
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
- 2,随后是判断这个加了Import注解的类,同时实现了 ImportSelector 接口,如果是的话,那么会先实例化一个 ImportSelector 组件,然后通过递归的方式,将一个ImportSelector组件解析成上面多个 Import 组件,最后又调用这个 processImports 方法,将解析成多个的import组件走上面的这个else逻辑
java
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
//实例化我们的SelectImport组件
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
//调用相关的aware方法
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
//判断是不是延时的DeferredImportSelectors,是这个类型 不进行处理
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {//不是延时的
//调用selector的selectImports
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
// 所以递归解析-- 直到成普通组件
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
- 3,最后再看这个加了Import注解,同时也实现了 ImportBeanDefinitionRegistrar 接口的方法,他和上面这个实现 ImportSelector 的方式不同,不会解析转成多个Import,而是作为一个属性加入到 ConfigurationClass配置类中
java
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
Class<?> candidateClass = candidate.loadClass();
//实例话我们的ImportBeanDefinitionRegistrar对象
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
//保存我们的ImportBeanDefinitionRegistrar对象 currentSourceClass=所在配置类
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
2.4. @ ImportResource 解析
接下来就是处理这个 ImportResource 注解,其主要实现如下
java
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
其核心就在于下面这句,用于获取本地的一些环境变量或者一些属性,比如获取一些 xml 文件中的一些配置属性等,然后将内容读取到配置文件
java
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
最后也是作为一个属性加入到 ConfigurationClass 配置文件中,和上面实现这个 ImportBeanDefinitionRegistrar 接口的一样,目前只是将这些属性加入到配置文件中,还有生成具体的beanDefinition
java
configClass.addImportedResource(resolvedResource, readerClass);
2.5. @Bean 解析
接下来就是接下这个 @Bean注解,这里也比较简单,就是在类中获取全部带有 @Bean的注解,然后去遍历这些元数据,然后将这些打包成一个 BeanMethod实体,加入到ConfigurationClass配置文件中
java
// 获取全部带有 @Bean的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
除了在类中获取 @Bean 注解之外,他还有一个在接口中获取该注解的解析。比如有的接口的default方法中加了这个注解进行注入,但其实现和在类中找一致,也是将这些打包成BeanMethod实体类,加入到配置文件中
java
processInterfaces(configClass, sourceClass);
private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
for (SourceClass ifc : sourceClass.getInterfaces()) {
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
for (MethodMetadata methodMetadata : beanMethods) {
if (!methodMetadata.isAbstract()) {
// A default method or other concrete method on a Java 8+ interface...
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
}
processInterfaces(configClass, ifc);
}
}
2.6. @父类解析
最后就剩一个父类解析,因为很多地方涉及到递归解析,判断父类是否也是一个配置类,因此需要在此处进行处理

递归直到没有父类需要解析,那么就会返回一个null,前面的while需要通过这个返回的null跳出递归,可以参考源码实现。
java
// 处理配置类的父类的 ,循环再解析
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// 没有父类解析完成
return null;
3,注册配置类的beanDefinition
上面PropertySource 、ComponentScan 、Import 都注册成了对应的beanDefinition,而@Bean、@ImportResource 注解目前只是封装成了实体加载到了配置类中,还没有进行注册到对应beanDefinition中,那么继续看源码,底层是如何将后面加到配置类的几个注册成bean定义的
还是得回到 processConfigBeanDefinitions 中,查看这个@Bean和实现了这个 ImportBeanDefinitionRegistrar 的 @Import方法时如何解析成BeanDefinition的,这块内容在前面讲解bean的生命周期的时候详细的讲过,这个也是前面的一个流程
java
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// 此处才把@Bean的方法和@Import 注册到BeanDefinitionMap中
this.reader.loadBeanDefinitions(configClasses);
}
接下来直接查看这个 loadBeanDefinitions 方法,里面也没做特别的事情,就是一个循环
java
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
//注册我们的配置类到容器中
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
接下来重点查看这个 loadBeanDefinitionsForConfigurationClass 方法,在这个方法中,对@Import、@Bean、@ImportResources、@ImportBeanDefinition 注解进行解析,然后生成对应的beanDefinition

3.1. @Import注解生成BeanDefinition
首先会判断这个配置类是不是导入进来的
java
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
如果这个类是通过Import导入进来的,那么会调用这个 registerBeanDefinitionForImportedConfigurationClass 方法来将这个类注册成 BeanDefinition,会设置一些元数据信息,bean定义,作用域等,最后通过注册的方式将这个实体类注册成一个BeanDefinition

3.2. @Bean注解生成BeanDefinition
随后会判断这个配置类上面是不是通过@Bean的方式注入进来的,如果是通过@Bean方式注入进来的,前面已经将@Bean的配置类包装成了 BeanMethod 实体类,因此只需要遍历这个配置类中的 BeanMethods实体类集合即可
java
//是不是通过我们的@bean导入进来的组件
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
这个解析会相对复杂点,里面设计多重校验,最后注册成BeanDefinition
java
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
//设计大量的校验`
...
// 最后注册成BeanDefinition中
this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}
3.3. @ImportResources注解生成BeanDefinition
随后判断是不是importResources注解注入进来的
java
private void loadBeanDefinitionsFromImportedResources(){
...
}
主要是加载一些grovvy配置文件或者xml文件,然后将这些配置文件设置到环境变量中

最后将这个配置文件通过reader读取的方式加载到配置文件中
java
reader.loadBeanDefinitions(resource);