一、前言
文章目录:Spring源码分析:文章目录
ConfigurationClassPostProcessor
是非常重要的一个 后处理器。 ConfigurationClassPostProcessor
完成了 配置类的解析和保存以及@Component
注解、@Import
等注解的解析工作 。将所有需要注入的bean解析成BeanDefinition
保存到BeanFactory
中。
首先来讲解一下ConfigurationClassPostProcessor
的结构图如下。
可见ConfigurationClassPostProcessor
接口实现了BeanDefinitionRegistryPostProcessor
(BeanFactory
的后处理器)
PriorityOrdered
(设置自己的优先级为最高) 和各种Aware
接口。
在Springboot
启动后,会通过SpringApplication#createApplicationContext()
来创建应用上下文,默认请情况下我们一般创建AnnotationConfigServletWebServerApplicationContext
作为应用上下文。而在AnnotationConfigServletWebServerApplicationContext
构造函数中会创建AnnotatedBeanDefinitionReader
。而在AnnotatedBeanDefinitionReader
构造函数中会调用 AnnotationConfigUtils#registerAnnotationConfigProcessors(this.registry);
,该方法将一些必要Bean(如ConfigurationClassPostProcessor
、AutowiredAnnotationBeanPostProcessor
、CommonAnnotationBeanPostProcessor
等)注入到了容器中。
我们这里重点看的是BeanDefinitionRegistryPostProcessor
接口的两个方法:
// 完成对 @Bean 方法的代理
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
// 允许在Spring容器启动后,在下一个阶段开始前,添加BeanDefinition的定义
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
关于这两个方法的调用时机和作用,我们在之前的文章(Spring源码分析五:BeanFactoryPostProcessor的处理 - invokeBeanFactoryPostProcessors)已经讲过,调用过程主要是在 Spring容器刷新的过程中,其中postProcessBeanDefinitionRegistry
方法先于 postProcessBeanFactory
方法被调用。
通过本篇分析ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()
方法。得知了ConfigurationClassPostProcessor
解析配置类(这里的配置类不仅仅局限于@Configuration
注解,还包括 @Import、 @ImportResource
等注解),将解析到的需要注入到Spring容器中的bean的BeanDefinition
保存起来。在后面的bean 初始化都需要BeanDefinition
。
ConfigurationClassPostProcessor#postProcessBeanFactory
方法通过cglib代理配置类,来拦截@Bean
修饰的方法。这么做的目的是为了在配置类中多次调用@Bean
方法返回的是同一个结果。即在下面的代码中demoController()
和demoController2()
方法中调用的demoService()
方法返回的结果是同一个值。避免了单例模式下的多例创建。
二 、入口 - invokeBeanFactoryPostProcessors()
首先Spring容器的启动我们debug进入的是Spring的容器刷新方法:refresh()
,接着我们F7进入子方法invokeBeanFactoryPostProcessors()
该方法就是激活各种BeanFactory后处理器(调用在上下文中注册为bean的工厂处理器(BeanFactoryPostProcessor))。具体代码如下:
AbstractApplicationContext#invokeBeanFactoryPostProcessors()
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
// getBeanFactoryPostProcessors方法获取了所有硬编码的bean工厂处理器
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
// 默认情况下这里判断不会为空,在refresh方法调用的prepareBeanFactory方法内已经执行过这段代码了
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
// 添加bean后置处理器,负责调用实现了LoadTimeWeaverAware接口setLoadTimeWeaver方法的bean
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// 添加一个临时类加载器
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
invokeBeanFactoryPostProcessors()>>>
PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors()
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
// 记录所有即将或已经处理的beanName,用于防止重复处理
Set<String> processedBeans = new HashSet<>();
// 对BeanDefinitionRegistry类型的处理,这里是交由BeanDefinitionRegistryPostProcessor来处理
// 这里判断BeanFactory如果是BeanDefinitionRegistry子类 则需要进行BeanDefinitionRegistryPostProcessor的处理,否则直接按照BeanFactoryPostProcessor处理即可。
// 关于为什么BeanDefinitionRegistry比较特殊上面也说过,因为BeanDefinitionRegistryPostProcessor只能处理BeanDefinitionRegistry的子类,所以这里需要区分是否是BeanDefinitionRegistry类型
if (beanFactory instanceof BeanDefinitionRegistry) {
// 下面逻辑看似复杂,其实就两步:
// 1. 获取所有硬编码的BeanDefinitionRegistryPostProcessor类型,激活postProcessBeanDefinitionRegistry方法
// 2. 获取所有配置的BeanDefinitionRegistryPostProcessor,激活postProcessBeanDefinitionRegistry方法
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
// 记录通过硬编码方式注册的BeanFactoryPostProcessor类型的处理器
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
// 记录所有注册的BeanDefinitionRegistryPostProcessor类型的处理器(含有硬编码注册的和配置注入注册的)
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
// 遍历硬编码注册的后处理器(都保存AbstractApplicationContext#beanFactoryPostProcessors中,这里通过参数beanFactoryPostProcessors传递过来)
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
// 将硬编码注册BeanDefinitionRegistryPostProcessor放到registryProcessors中
registryProcessors.add(registryProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
// 一个临时变量,记录通过配置方式注册的BeanDefinitionRegistryPostProcessor类型的处理器
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
// 获取所有的实现了BeanDefinitionRegistryPostProcessor的beanName
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
// 筛选出实现了PriorityOrdered接口的实现类,优先执行
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
// 记录到currentRegistryProcessors中
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
// 进行排序
sortPostProcessors(currentRegistryProcessors, beanFactory);
// 以配置方式注册的且实现了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor集合全部放到registryProcessors中
registryProcessors.addAll(currentRegistryProcessors);
// 激活 postProcessBeanDefinitionRegistry 方法
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
// 清空临时变量currentRegistryProcessors的内容
currentRegistryProcessors.clear();
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
// 筛选出实现了Ordered接口的实现类,第二执行
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
// 排序
sortPostProcessors(currentRegistryProcessors, beanFactory);
// 以配置方式注册的且实现了Ordered接口的BeanDefinitionRegistryPostProcessor集合全部放到registryProcessors中
registryProcessors.addAll(currentRegistryProcessors);
// 激活
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
// 清空临时变量currentRegistryProcessors的内容
currentRegistryProcessors.clear();
// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
// 最后获取No-Ordered(没有实现排序)接口的 BeanDefinitionRegistryPostProcessor ,进行激活。
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
// 排序
sortPostProcessors(currentRegistryProcessors, beanFactory);
// 将配置方式注册的且没有实现排序(No-Ordered)接口的BeanDefinitionRegistryPostProcessor集合全部放到registryProcessors中
registryProcessors.addAll(currentRegistryProcessors);
// 激活
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
// 清空临时变量currentRegistryProcessors的内容
currentRegistryProcessors.clear();
}
// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
// 到这里,所有的 BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法都已经激活结束。
// 开始激活postProcessBeanFactory方法
// 因为BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子类,所有这里激活的是BeanDefinitionRegistryPostProcessor的postProcessBeanFactory()方法
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
// regularPostProcessors中记录的是硬编码注入的BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}
else {
// Invoke factory processors registered with the context instance.
// 如果 beanFactory instanceof BeanDefinitionRegistry = false,那么BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry()方法并不生效,就直接激活postProcessBeanFactory()方法即可。
// 激活硬编码注册的BeanFactoryPostProcessor.postProcessBeanFactory()方法
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
// 到这一步,所有的硬编码方式注入的后处理器都处理完毕; 配置注入的BeanDefinitionRegistryPostProcessor后处理器的postProcessBeanDefinitionRegistry()和postProcessBeanFactory()方法都已经激活。
// 下面开始处理配置注入的BeanFactoryPostProcessor的postProcessBeanFactory后处理器。
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// 获取所有后处理器的beanName,用于后面处理
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
// 创建几个保存不同排序的集合,按照实现的排序接口调用
// 筛选出实现了排序接口PriorityOrdered的类
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
// 筛选出实现了排序接口Ordered的类
List<String> orderedPostProcessorNames = new ArrayList<>();
// 筛选出未实现排序接口的类
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
}
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
// 排序和激活(实现了PriorityOrdered接口的后处理器)
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
// 排序和激活(实现了Ordered接口的后处理器)
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// Finally, invoke all other BeanFactoryPostProcessors.
// 排序和激活(没有实现排序接口的后处理器)
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// Clear cached merged bean definitions since the post-processors might have
// modified the original metadata, e.g. replacing placeholders in values...
// 清除元数据缓存
beanFactory.clearMetadataCache();
}
从代码中我们可以看到激活 postProcessBeanDefinitionRegistry()
方法
invokeBeanFactoryPostProcessors() >>>
invokeBeanDefinitionRegistryPostProcessors()
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
下面我们就来解析一下这个主要方法postProcessBeanDefinitionRegistry()
三、ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
// 关键方法,解析 配置类的定义
processConfigBeanDefinitions(registry);
}
可以看到postProcessBeanDefinitionRegistry()
方法中并没有处理什么逻辑,真正逻辑在其调用的 processConfigBeanDefinitions
方法中
1、解析配置类的定义 - processConfigBeanDefinitions
processConfigBeanDefinitions
方法完成了关于配置类的所有解析。
需要注意的是,到达这一步的时候, Springboot 启动类已经被解析成BeanDefinition 注册到容器中。
具体代码如下:
ConfigurationClassPostProcessor#processConfigBeanDefinitions()
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 获取已经解析的BeanName。这里需要注意的是,Springboot的话,启动类已经被注册。具体的注册时机是在Springboot启动时候的SpringApplication#prepareContext方法中。
String[] candidateNames = registry.getBeanDefinitionNames();
// 遍历BeanName
for (String beanName : candidateNames) {
// 获取BeanDefinition
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 如果bean被解析过(Bean被解析后会在beanDef中设置属性CONFIGURATION_CLASS_ATTRIBUTE),if条件成立,这里是为了防止重复解析
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
// 1. ConfigurationClassUtils.checkConfigurationClassCandidate 解析了当前bean是否是配置类,关于其详细内容,后面解析 需要注意的是,
// 本文所说的配置类即使满足 full 或 lite 条件的类,而不仅仅是被 @Configuration 修饰的类。
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
// 添加到配置类集合中
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
// 如果没有找到配置类,则直接返回,不需要下面的解析
if (configCandidates.isEmpty()) {
return;
}
// Sort by previously determined @Order value, if applicable
// 按照@Order 注解进行排序(如果使用了 @Order 注解的话)
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
// 判断如果是registry是SingletonBeanRegistry类型,则从中获取beanName生成器(BeanNameGenerator)。
// 实际上这里是register类型是DefaultListableBeanFactory。是SingletonBeanRegistry的子类
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
// 如果环境变量为空则指定一个标准环境,这里是StandardServletEnvironment类型,在前面的启动篇我们可以知道。
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// Parse each @Configuration class
// 下面开始解析每一个配置类
// 准备配置类的解析类ConfigurationClassParser
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
// 用来保存尚未解析的配置类
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
// 用来保存已经解析的配置类
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
// do..while 循环解析。因为一个配置类可能引入另一个配置类,需要循环解析,直至没有其他需要解析的类。
do {
// 2. 开始解析。后面详细分析
parser.parse(candidates);
// 3. 这里的校验规则是如果是被@Configuration修饰且proxyBeanMethods属性为true,则类不能为final。如果@Bean修饰的方法,则必须是可覆盖的.
// 因为@Configuration(proxyBeanMethods = true) 是需要cglib代理的,所以不能为终态, @Bean所修饰的方法也有一套约束规则,下面详细讲
// 是否需要代理是根据 类或方法上的 @Scope 注解指定的,默认都是不代理
parser.validate();
// configClasses保存这次解析出的配置类。此时这些ConfigurationClass中保存了解析出来的各种属性值,等待最后构建BeanDefinition
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
// 去除已经解析过的配置类
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
// 4. 注册bean
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
// if条件如果成立,说明有新的bean注册了,则需要解析新的bean
if (registry.getBeanDefinitionCount() > candidateNames.length) {
// 获取新的beanName
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
// 过滤出未解析的bean检测是否是未解析过的配置类
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
// 如果是未解析的配置类,则保存到candidates中
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
// 如果 candidates 不为空,则说明有未被解析的配置类,循环解析。
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
// 到这里已经把配置类解析完毕了。
// 将ImportRegistry 注册为 bean,以支持ImportAware @Configuration 类
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
// 清除缓存
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
这里简单总结一下流程;
- 获取已经注册的Bean, 并筛选出配置类,按照
@Order
进行排序,得到配置类集合configCandidates
- 调用
parser.parse(candidates);
对配置类进行解析 - 调用
this.reader.loadBeanDefinitions(configClasses);
进行配置类的注册 - 检验
registry.getBeanDefinitionCount() > candidateNames.length
是否成立。这里由于第三步会将新解析出来的bean进行注册,如果这里成立,则说明有新的配置类完成了注册,获取到新注册的配置类candidateNames。循环从第二步重新解析,直到没有新注入的配置类。
上面解释的可能比较乱,因为我们下面详细去分析几个方法。
1.1 校验是否是配置类 - checkConfigurationClassCandidate()
ConfigurationClassUtils#checkConfigurationClassCandidate()
在processConfigBeanDefinitions
方法中。判断一个类是否是配置类就是通过checkConfigurationClassCandidate
方法来判断的,那么我们需要看看这个方法中是怎么实现的。
在这个方法里,关键的部分是 给BeanDefinition
设置了CONFIGURATION_CLASS_ATTRIBUTE
为 full 或者 lite 设置这两个属性标识,如果一个类满足full或 lite的条件,则会被认为是配置类。需要注意的是,本文所说的配置类即使满足 full 或 lite 条件的类,而不仅仅是被@Configuration
修饰的类
首先需要注意的是,在checkConfigurationClassCandidate()方法
中,配置类的类型分为两种,Full 和 Lite,即完整的配置类和精简的配置类。
full 和 lite 设置的规则如下:
Full
: 即类被 @Configuration 注解修饰 && proxyBeanMethods属性为true (默认为 true)Lite
: 被 @Component、@ComponentScan、@Import、@ImportResource 修饰的类 或者 类中有被@Bean修饰的方法。
Full 配置类就是我们常规使用的配置类
Lite 配置类就是一些需要其他操作引入一些bean 的类
下面我们来看具体代码:
ConfigurationClassUtils#checkConfigurationClassCandidate()
public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
// 获取className
String className = beanDef.getBeanClassName();
//如果beanDef名字为空,或者beanDef中工厂方法名字factoryMethodName不为空,则直接返回false
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
// 解析关于当前被解析类的 注解元数据
AnnotationMetadata metadata;
// 如果当前BeanDefinition是AnnotatedBeanDefinition(相较于一般的BeanDefinition,他多了一些注解信息的解析)类型。直接获取注解元数据即可
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
// Can reuse the pre-parsed metadata from the given BeanDefinition...
//获取beanDef注解元数据
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}
else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
// Check already loaded Class if present...
// since we possibly can't even load the class file for this Class.
/**
* 排除我们启动时注册的几个spring内部类(BeanFactoryPostProcessor、BeanPostProcessor、AopInfrastructureBean、EventListenerFactory),这几个内部类均为下面几个接口的实现或者扩展
* 第一次检查配置类会排除spring内部类,之后我们自定义的符合这几个接口的类不会进入逻辑,一般是AnnotatedBeanDefinition的实现
* 所有不会被排除
*/
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
// 获取注解元数据
metadata = new StandardAnnotationMetadata(beanClass, true);
}
else {
// 按照默认规则解析
try {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
metadata = metadataReader.getAnnotationMetadata();
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not find class file for introspecting configuration annotations: " + className, ex);
}
return false;
}
}
/**
* 该版本代码的和其他版本代码有点不同,我这边写出来了
* // 获取Configuration注解信息
* Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
*/
/**
* // 1、如果该beanDef存在@Configuration,并且@Configuration中proxyBeanMethods为true,则设置为CONFIGURATION_CLASS_FULL:full,即全注解
* // 2、proxyBeanMethods默认为true,即对于全注解配置类中的@Bean方法生成bean时会通过代理子类也即cglib来生成bean,而不是直接调用@Bean方法,主要时在子类中首先判断是否已经生成了该bean,防止生成多个bean
* if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
* beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
* }
*/
if (isFullConfigurationCandidate(metadata)) {
// 设置 CONFIGURATION_CLASS_ATTRIBUTE 为 full
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
/**
* 执行这一步存在以下几种情况
* 1 config != null但是该配置类不存在@Bean修饰的方法
* 2 config == null 但是符合isConfigurationCandidate条件如下
* 2.1 不是接口
* 2.2 被下面几个注解修饰
* candidateIndicators.add(Component.class.getName());
* candidateIndicators.add(ComponentScan.class.getName());
* candidateIndicators.add(Import.class.getName());
* candidateIndicators.add(ImportResource.class.getName());
* 2.3 存在@Bean修饰的方法
* 符合2.1且满足2.2 其中一个或者2.3则该类是设置为CONFIGURATION_CLASS_LITE:lite即使半注解配置类
*
*/
else if (isLiteConfigurationCandidate(metadata)) {
// 设置 CONFIGURATION_CLASS_ATTRIBUTE 为 lite
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
// It's a full or lite configuration candidate... Let's determine the order value, if any.
Integer order = getOrder(metadata);
// 如果存在order则可以设置该属性,以后可以进行排序设定执行顺序
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
}
主要是检测有没有对应的注解@Configurable
、@Component
、@ComponentScan
、@Import
、@ImportResource
和@Bean
标注的方法,并根据相应的注解和条件设置属性为full:
全注解和lite:
半注解。
1.1.1 是否是半注解 - isLiteConfigurationCandidate()
ConfigurationClassUtils#isLiteConfigurationCandidate()
在上面的代码中,我们看到判断是否是 Lite 的关键方法是isLiteConfigurationCandidate()
。其代码如下:
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
// 不能是接口
if (metadata.isInterface()) {
return false;
}
// Any of the typical annotations found?
// 被 candidateIndicators 中的注解修饰。其中 candidateIndicators(@Component、@ComponentScan、@Import、@ImportResource) 注解在静态代码块中加载了
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
// Finally, let's look for @Bean methods...
try {
// 类中包含被 @Bean 注解修饰的方法
return metadata.hasAnnotatedMethods(Bean.class.getName());
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
}
return false;
}
}
1.2 开始解析 - parser.parse(candidates);
ConfigurationClassParser#parse(Set configCandidates)
上面解析了如何判断一个类是否是配置类。也完成了配置类的筛选。那么开始进行配置类的解析,在 processConfigBeanDefinitions()
方法中,对配置类的解析也只是一句话完成:
parser.parse(candidates);
parser.parse(candidates);
的作用是:
- 将所有的配置类保存到
ConfigurationClassParser.configurationClasses
集合中
private final Map<ConfigurationClass, ConfigurationClass> configurationClasses = new LinkedHashMap<>();
- 解析注解并赋值给每个
ConfigurationClass
对应的属性。如解析@Import
注解,并通过如下语句将结果保存到ConfigurationClass.importBeanDefinitionRegistrars
集合中。
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
同样的还有 将@ImportResource
注解保存到ConfigurationClass.importedResources
中,将@Bean修饰的方法和接口静态方法保存到ConfigurationClass.beanMethods
中。
而在之后的this.reader.loadBeanDefinitions(configClasses);
中才进行了这些属性的进一步处理
下面我们来具体看代码,其代码如下:
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<>();
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
// 针对不同类型的 BeanDefinition 做一些处理
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
processDeferredImportSelectors();
}
里面的 parse 方法殊途同归,最终都会调用 processConfigurationClass()
方法,所以我们直接进入该方法:
ConfigurationClassParser#processConfigurationClass(ConfigurationClass configClass)
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
// 判断是否应该跳过当前类的解析。这里面解析了 @Conditional 注解
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// 判断是否已经解析过。configurationClasses 中保存着已经解析过的配置类。在下面解析过的类都会被保存到 configurationClasses 中
// 这里应该是 注入的配置类优先级高于引入的配置类
// 如果配置类被多次引入则合并属性
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
// 一个类被重复解析,那么可能被重复引入了,可能是通过 @Import 注解或者嵌套在其他配置类中被引入。如果这两者都是通过这种方式被引入,那么则进行引入合并
// 如果当前配置类和之前解析过的配置类都是引入的,则直接合并
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
// 否则,忽略新导入的配置类;现有的非导入类将覆盖它
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
// 如果当前的配置类不是引入的,则移除之前的配置类,重新解析
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
// 保存解析过的 配置类
this.configurationClasses.put(configClass, configClass);
}
这里需要注意的是配置类的重复引入优先级的问题 :
一般来说,Spring有一个自己的规则 :自身注入方式 优先于 引入方式。这里的引入方式指的被 @Import 或者其他配置类引入。当一个类被多次引入时,会使用自身注入的方式的bean 替代 被引入方式的bean。如果二者都是引入方式,则进行合并(在 ConfigurationClass 类中有一个importedBy 集合,将新引入的来源保存到 importedBy 中)
ConfigurationClass#mergeImportedBy(ConfigurationClass otherConfigClass)
public void mergeImportedBy(ConfigurationClass otherConfigClass) {
this.importedBy.addAll(otherConfigClass.importedBy);
}
看了这么久的源码,也知道了Spring的套路,方法名以do开头的才是真正做事的方法, 所以我们来看 doProcessConfigurationClass()
方法。
ConfigurationClassParser#doProcessConfigurationClass()
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// Recursively process any member (nested) classes first
// 1. 处理 @Component 注解
processMemberClasses(configClass, sourceClass);
// Process any @PropertySource annotations
// 2. 处理 @PropertySource 注解
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// Process any @ComponentScan annotations
// 3. 处理 @ComponentScan注解
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
// 4. 处理 @Import 注解
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any @ImportResource annotations
// 5. 处理 @ImportResource 注解
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);
}
}
// Process individual @Bean methods
// 6. 处理 @Bean修饰的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
// 7. 处理其他默认接口方法
processInterfaces(configClass, sourceClass);
// Process superclass, if any
// 处理父类,如果存在
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();
}
}
// No superclass -> processing is complete
return null;
}
doProcessConfigurationClass
方法中的逻辑很清楚,因为他把大部分的逻辑直接封装成了方法。下面我们就来一个一个分析。
1.2.1 处理@Component注解 - processMemberClasses()
这里对@Component
的处理其实是处理配置类的内部类,即如果当前类是被 @Component
修饰,则需要判断其内部类是否需要解析。
processMemberClasses()
方法的代码如下:
代码逻辑也很简单。即如果配置类中有内部类,则判断其内部类是否是配置类,如果是则递归去解析新发现的内部配置类。
ConfigurationClassParser#processMemberClasses()
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
// 获取内部类
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
if (!memberClasses.isEmpty()) {
// 如果有内部类,则遍历内部类,判断内部类是否是配置类,如果是,则添加到 candidates 集合中。
List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
for (SourceClass memberClass : memberClasses) {
// 这里判断的是是否是lite 类型的配置类
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
candidates.add(memberClass);
}
}
// 进行排序
OrderComparator.sort(candidates);
for (SourceClass candidate : candidates) {
// importStack 用来缓存已经解析过的内部类,这里处理循环引入问题。
if (this.importStack.contains(configClass)) {
// 打印循环引用异常
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
// 解析前入栈,防止循环引入
this.importStack.push(configClass);
try {
// 递归去解析新发现的配置类
processConfigurationClass(candidate.asConfigClass(configClass));
}
finally {
// 解析完毕出栈
this.importStack.pop();
}
}
}
}
}
注:
- 判断内部类是否是配置类,使用的方法是 ConfigurationClassUtils.isConfigurationCandidate,这里是检测内部类是否满足lite 的配置类规则,并未校验 full的规则。
- 代码中使用了this.importStack 来防止递归引入。避免了A引入B,B又引入A这种无限循环的情况。
1.2.2 处理 @PropertySource 注解
@PropertySource
注解可以引入配置文件使用。在这里进行@PropertySource
注解的解析,将引入的配置文件加载到环境变量中
// 去重后遍历 PropertySource 注解所指向的属性。注意这里有两个注解@PropertySources 和 @PropertySource。
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
// 解析PropertySource 注解
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
processPropertySource
代码如下,在这里解析每一个@PropertySource
注解属性 :
ConfigurationClassParser#processPropertySource()
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
// 获取 @PropertySource 注解的各个属性
String name = propertySource.getString("name");
if (!StringUtils.hasLength(name)) {
name = null;
}
String encoding = propertySource.getString("encoding");
if (!StringUtils.hasLength(encoding)) {
encoding = null;
}
// 获取指向的文件路径
String[] locations = propertySource.getStringArray("value");
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
// 遍历文件路径
for (String location : locations) {
try {
// 根据路径获取到资源文件并保存到environment 中
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
// 解决占位符,获取真正路径
Resource resource = this.resourceLoader.getResource(resolvedLocation);
//保存 PropertySource 到 environment 中
addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
}
catch (IllegalArgumentException | FileNotFoundException | UnknownHostException | SocketException ex) {
// Placeholders not resolvable or resource not found when trying to open it
if (ignoreResourceNotFound) {
if (logger.isInfoEnabled()) {
logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
}
}
else {
throw ex;
}
}
}
}
1.2.3 处理 @ComponentScan、@ComponentScans 注解
@componentScans
指定自动扫描的路径。
// 这里会将 @ComponentScans 中的多个 @ComponentScan 也解析出来封装成一个个AnnotationAttributes对象
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
// 如果当前配置类被 @componentScans 或 @componentScan 注解修饰 && 不应跳过
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
// 遍历 @ComponentScans、 @ComponentScan
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
// 直接执行扫描,根据指定路径扫描出来bean。
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
// 遍历扫描出来的bean
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
// 获取原始的bean的定义
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
// 检测如果是配置类,则递归调用 parse 解析。
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
这里需要注意 :
-
this.componentScanParser.parse()
方法完成了指定路径下的bean的扫描,这里不再具体分析。 -
这里校验是否是配置类调用的是
checkConfigurationClassCandidate
方法,即校验了 full或lite的规则,和 处理@Component
中的内部类的规则并不相同。 -
没错,又是递归,如果扫描到的bean中发现了新的配置类,则递归去解析。
-
之前的我们提过,Springboot 在启动过程中将 启动类注册到了容器中,那么在这里进行递归遍历的时候就会通过启动类指定的默认路径来进行遍历, 完成了Springboot的启动注册。
1.2.4 处理 @Import、ImportSelector、ImportBeanDefinitionRegistrar
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
该方法处理的包括@Import、ImportSelector、 ImportBeanDefinitionRegistrar
。这三个注解或接口都可以完成Bean的引入功能。
@Import:可以通过 @Import(XXX.class)
的方式,将指定的类注册到容器中
ImportSelector
: Spring会将 ImportSelector#selectImports())
方法返回的内容通过反射加载到容器中
ImportBeanDefinitionRegistrar
: 可以通过registerBeanDefinitions()
方法声明BeanDefinition 并自己注册到Spring容器中 比如 : MyBatis 中的 AutoConfiguredMapperScannerRegistrar
对@Mapper
修饰类的注册过程
需要注意的是,这里解析的ImportSelector、ImportBeanDefinitionRegistrar
都是通过 @Import
注解引入的。如果不是通过@Import
引入(比如直接通过@Component
将ImportSelecto
r、ImportBeanDefinitionRegistrar
注入)的类则不会被解析。
注意 getImports(sourceClass)
方法的作用是解析@Import
注解
我们直接来看 processImports 方法,注释都比较清楚 :
ConfigurationClassParser#processImports()
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
// importCandidates 是通过getImports() 方法解析 @Import 注解而来, 如果为空则说明没有需要引入的直接返回
if (importCandidates.isEmpty()) {
return;
}
// 检测是否是循环引用。
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
// 解析前先入栈,防止循环引用
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
// 判断是否是ImportSelector类型。ImportSelector 则需要调用selectImports 方法来获取需要注入的类。
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {
// 调用 selectImports 方法获取需要引入的类,并递归再次处理。
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
// 递归解析
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
// 如果是 ImportBeanDefinitionRegistrar 类型,则委托它注册其他bean定义
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// 否则递归处理需要引入的类。
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
1.2.5 处理 @ImportResource 注解
@ImportResource
就显得很简单了,直接保存到configClass 中
// 5. 处理 @ImportResource 注解
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);
}
}
1.2.6 处理 @Bean修饰的方法
@Bean
也很简单了,直接保存到 configClass 的中
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
1.2.7 处理接口默认方法
这里是 检测 配置类实现的接口中的默认方法是否被@Bean修饰,如果被修饰则也需要保存到 configClass
中
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);
}
}
1.2.8 处理父类
如果存在父类,则将父类返回,对父类进行解析。
// Process superclass, if any
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();
}
}
这里这么处理是解析到最上层的父类。这里理一下调用顺序:parse -> processConfigurationClass -> doProcessConfigurationClass 。而 doProcessConfigurationClass
有如下一个循环,只有sourceClass = null
才会跳出循环。当configClass
没有满足上面判断条件的父类时,才会返回null
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
1.3 parser.validate();
到了 这一步,是对解析出来的配置类进行进一步的校验,确保没有问题
这里我们看看其代码如下:
ConfigurationClassParser#validate()
public void validate() {
for (ConfigurationClass configClass : this.configurationClasses.keySet()) {
configClass.validate(this.problemReporter);
}
}
这里可以看到是调用每个 ConfigurationClass
类的 validate
方法进行校验,我们进去看看ConfigurationClass#validate
的代码 :
public void validate(ProblemReporter problemReporter) {
// A configuration class may not be final (CGLIB limitation) unless it declares proxyBeanMethods=false
// 获取 @Configuration 注解的属性信心
Map<String, Object> attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName());
// 如果 @Configuration 存在(attributes != null) && attributes.get("proxyBeanMethods") == true 才进行进一步的校验
if (attributes != null && (Boolean) attributes.get("proxyBeanMethods")) {
// 如果配置类 是 final 修饰,即终态类,则是错误,因为无法动态代理
if (this.metadata.isFinal()) {
problemReporter.error(new FinalConfigurationProblem());
}
// 对配置类中的 @Bean 注解修饰的方法进行校验
for (BeanMethod beanMethod : this.beanMethods) {
beanMethod.validate(problemReporter);
}
}
}
这里我们再来看看@Bean
方法的校验 BeanMethod#validate
如下:
@Override
public void validate(ProblemReporter problemReporter) {
// 如果是静态方法没有约束规则,直接返回。
if (getMetadata().isStatic()) {
// static @Bean methods have no constraints to validate -> return immediately
return;
}
// 校验该方法所属的类是否被 @Configuration 修饰。
if (this.configurationClass.getMetadata().isAnnotated(Configuration.class.getName())) {
// 判断是否可重写。cglib代理需要方法可重写。不可重写则错误
if (!getMetadata().isOverridable()) {
// instance @Bean methods within @Configuration classes must be overridable to accommodate CGLIB
problemReporter.error(new NonOverridableMethodError());
}
}
}
1.4 this.reader.loadBeanDefinitions(configClasses);
上面也说了,在parser.parse(candidates);
方法中,将各种注解的属性值都解析了出来,并保存到了 configClass的各种属性中。而在 this.reader.loadBeanDefinitions(configClasses);
中才真正处理了这些属性。所以我们接下来看看loadBeanDefinitions 的处理流程。
loadBeanDefinitions
遍历了每一个ConfigurationClass
,通过loadBeanDefinitionsForConfigurationClass()
方法处理。
ConfigurationClassBeanDefinitionReader#loadBeanDefinitions()
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
所以我们来看看loadBeanDefinitionsForConfigurationClass
的实现。可很清楚的看到,每个部分的解析都封装到了不同的方法中。
loadBeanDefinitions() >>>
ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass()
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
// 判断是否应该跳过
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
// 1. 如果配置是被引入的(被 @Import 或者其他配置类内部引入)
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 2. 遍历配置类中的所有 BeanMethod方法
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// 3. 加载 通过 @ImportResource 的 获取的bean
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 4. 加载 通过 @Import 的 获取的bean
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
下面 我们来详细看看每个方法。
1.4.1 registerBeanDefinitionForImportedConfigurationClass
这一步的工作很简单,就是将引入的配置类注册为 BeanDefinition。
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
AnnotationMetadata metadata = configClass.getMetadata();
AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
configBeanDef.setScope(scopeMetadata.getScopeName());
String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
// 创建代理,根据 scopeMetadata 的代理模式。默认不创建代理。
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
// 注册了BeanBeanDefinition 。这里将BeanDefinition保存到了 DefaultListableBeanFactory#beanDefinitionMap 中
this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
configClass.setBeanName(configBeanName);
if (logger.isDebugEnabled()) {
logger.debug("Registered bean definition for imported class '" + configBeanName + "'");
}
}
这里需要注意的是 AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
根据scopeMetadata
的代理模式创建了代理。代理模式有四种,分别为
DEFAULT
: 默认模式。默认等同于NONO
: 不使用代理INTERFACES
: Jdk 动态代理TARGET_CLASS
: Cglib代理
在applyScopedProxyMode
方法中 通过获取ScopeMetadata.getScopedProxyMode()
来判断使用什么代理方式。而ScopeMetadata
的代理方式 是在创建scopeMetadata
的过程中,获取类上面的@Scope
的proxyMode
属性来指定的。
ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
resolveScopeMetadata
方法如下
ScopeMetadataResolver#resolveScopeMetadata()
ScopeMetadata resolveScopeMetadata(BeanDefinition definition);
AnnotationScopeMetadataResolver#resolveScopeMetadata()
@Override
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
ScopeMetadata metadata = new ScopeMetadata();
if (definition instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
// 获取 @Scope 注解
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
annDef.getMetadata(), this.scopeAnnotationType);
if (attributes != null) {
metadata.setScopeName(attributes.getString("value"));
// 获取 @Scope 的proxyMode属性
ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
if (proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = this.defaultProxyMode;
}
// 设置 scopedProxyMode 属性,后面根据此属性判断使用什么代理方式
metadata.setScopedProxyMode(proxyMode);
}
}
return metadata;
}
1.4.2 loadBeanDefinitionsForBeanMethod
具体代码如下,基本上就是解析各种注解,创建对应的 BeanDefinition 并注册。
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata();
String methodName = metadata.getMethodName();
// Do we need to mark the bean as skipped by its condition?
// 是否应该跳过
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
configClass.skippedBeanMethods.add(methodName);
return;
}
if (configClass.skippedBeanMethods.contains(methodName)) {
return;
}
// 获取被 @Bean修饰的方法
AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
Assert.state(bean != null, "No @Bean annotation attributes");
// Consider name and any aliases
// 获取别名
List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
String beanName = (!names.isEmpty() ? names.remove(0) : methodName);
// Register aliases even when overridden
// 注册别名
for (String alias : names) {
this.registry.registerAlias(beanName, alias);
}
// Has this effectively been overridden before (e.g. via XML)?
// 判断是否已经被定义过
if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {
throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() +
"' clashes with bean name for containing configuration class; please make those names unique!");
}
return;
}
// 定义配置类的 BeanDefinition
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
beanDef.setResource(configClass.getResource());
beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
// 处理静态 @Bean 方法和 非静态 @Bean 方法
if (metadata.isStatic()) {
// static @Bean method
if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
}
else {
beanDef.setBeanClassName(configClass.getMetadata().getClassName());
}
// 设置唯一工厂方法名称
beanDef.setUniqueFactoryMethodName(methodName);
}
else {
// instance @Bean method
// 指定要使用的工厂bean(如果有)。这是用于调用指定工厂方法的bean的名称
beanDef.setFactoryBeanName(configClass.getBeanName());
// 设置唯一工厂方法名称,内部调用了 setFactoryMethodName(name); 保存 FactoryMethodName
beanDef.setUniqueFactoryMethodName(methodName);
}
if (metadata instanceof StandardMethodMetadata) {
beanDef.setResolvedFactoryMethod(((StandardMethodMetadata) metadata).getIntrospectedMethod());
}
// 设置构造模式 构造注入
beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
// 设置跳过属性检查
beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.
SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
// 处理通用的注解: @Lazy、@Primary、@DependsOn、@Role、@Description。设置到 BeanDefinition 中
AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);
// 获取注解的其他属性并设置到 BeanDefinition
Autowire autowire = bean.getEnum("autowire");
if (autowire.isAutowire()) {
beanDef.setAutowireMode(autowire.value());
}
boolean autowireCandidate = bean.getBoolean("autowireCandidate");
if (!autowireCandidate) {
beanDef.setAutowireCandidate(false);
}
String initMethodName = bean.getString("initMethod");
if (StringUtils.hasText(initMethodName)) {
beanDef.setInitMethodName(initMethodName);
}
String destroyMethodName = bean.getString("destroyMethod");
beanDef.setDestroyMethodName(destroyMethodName);
// Consider scoping
ScopedProxyMode proxyMode = ScopedProxyMode.NO;
// 处理方法上的 @Scope 注解
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class);
if (attributes != null) {
beanDef.setScope(attributes.getString("value"));
proxyMode = attributes.getEnum("proxyMode");
if (proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = ScopedProxyMode.NO;
}
}
// Replace the original bean definition with the target one, if necessary
// 如果有必要(代理模式不同),替换掉旧的BeanDefinition
BeanDefinition beanDefToRegister = beanDef;
if (proxyMode != ScopedProxyMode.NO) {
BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
new BeanDefinitionHolder(beanDef, beanName), this.registry,
proxyMode == ScopedProxyMode.TARGET_CLASS);
beanDefToRegister = new ConfigurationClassBeanDefinition(
(RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata);
}
if (logger.isTraceEnabled()) {
logger.trace(String.format("Registering bean definition for @Bean method %s.%s()",
configClass.getMetadata().getClassName(), beanName));
}
// 注册BeanDefinition
this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}
这里特意提一下下面几句的功能
// 设置引入该bean 的配置类的类名
beanDef.setFactoryBeanName(configClass.getBeanName());
// 设置 引入bean 的类名
beanDef.setBeanClassName(configClass.getMetadata().getClassName());
// 设置在配置类中引入该bean 的方法名
beanDef.setUniqueFactoryMethodName(methodName);
这里会为 @Bean
修饰的方法创建出一个ConfigurationClassBeanDefinition
注册到Spring
容器中,ConfigurationClassBeanDefinition
是特指用于表示从配置类(而不是其他任何配置源)创建了Bean
定义。在需要确定是否在外部创建bean定义的bean覆盖情况下使用。在后面的Bean实例化过程中,会有多次使用。比如在 AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation()
中
// 在 determineTargetType 方法中根据 factoryMethodName 是否为空,判断bean注入方式,来获取注入的 Class类型
Class<?> targetType = determineTargetType(beanName, mbd)
以及会在AbstractAutowireCapableBeanFactory#createBeanInstance()
方法中有如下两句。
而@Bean
的修饰的方法会调用instantiateUsingFactoryMethod()
方法,通过反射调用方法,并将反射结果注入到Spring容器中,完成@Bean
注解的功能。
1.4.3 loadBeanDefinitionsFromImportedResources
loadBeanDefinitionsFromImportedResources
从导入的资源加载Bean定义。即通过解析 @ImportResource
注解引入的资源文件,获取到BeanDefinition
并注册。
private void loadBeanDefinitionsFromImportedResources(
Map<String, Class<? extends BeanDefinitionReader>> importedResources) {
Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<>();
// 遍历引入的资源文件
importedResources.forEach((resource, readerClass) -> {
// Default reader selection necessary?
if (BeanDefinitionReader.class == readerClass) {
// 处理 .groovy 类型文件
if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {
// When clearly asking for Groovy, that's what they'll get...
readerClass = GroovyBeanDefinitionReader.class;
}
else {
// Primarily ".xml" files but for any other extension as well
// 这里使用 XmlBeanDefinitionReader 类型来解析
readerClass = XmlBeanDefinitionReader.class;
}
}
// 从缓冲中获取
BeanDefinitionReader reader = readerInstanceCache.get(readerClass);
// 如果缓存中没有,则创建一个 reader 用于 resource 的解析。
if (reader == null) {
try {
// Instantiate the specified BeanDefinitionReader
reader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);
// Delegate the current ResourceLoader to it if possible
if (reader instanceof AbstractBeanDefinitionReader) {
AbstractBeanDefinitionReader abdr = ((AbstractBeanDefinitionReader) reader);
abdr.setResourceLoader(this.resourceLoader);
abdr.setEnvironment(this.environment);
}
readerInstanceCache.put(readerClass, reader);
}
catch (Throwable ex) {
throw new IllegalStateException(
"Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");
}
}
// TODO SPR-6310: qualify relative path locations as done in AbstractContextLoader.modifyLocations
// 解析resource资源中的内容
reader.loadBeanDefinitions(resource);
});
}
1.4.4 loadBeanDefinitionsFromRegistrars
loadBeanDefinitionsFromRegistrars
方法注册了了@Import
注解引入的内容。这里很简单,将@Import
引入的内容注入到容器中。
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}
四、总结
从目前我看到的来说(虽然也没看过几个),有两个后处理器非常重要:
-
ConfigurationClassPostProcessor
: 即本文解析的这个后处理器。虽然仅仅是上篇,但是其作用已经非常清楚了。ConfigurationClassPostProcessor
解析配置类(这里的配置类不仅仅局限于@Configuration
注解,还包括@Import
、@ImportResource
等注解),将解析到的需要注入到Spring容器中的bean的BeanDefinition
保存起来。在后面的bean 初始化都需要BeanDefinition
。 -
AutowiredAnnotationBeanPostProcessor
: 之前解析过。完成了Bean
所依赖的属性的注入。 解析bean中的 需要自动注入的bean@Autowired
和@Inject @Value
注解。
简单来说ConfigurationClassPostProcessor
完成了 Bean的扫描与解析,AutowiredAnnotationBeanPostProcessor
完成了Bean 属性的注入
本文只分析了ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()
方法,对于ConfigurationClassPostProcessor#postProcessBeanFactory
方法的分析则放在后篇,这里简单的说明ConfigurationClassPostProcessor#postProcessBeanFactory()
方法通过cglib动态代理,完成了 对@Bean
修饰方法的代理,以确保其正确语义。
以上:内容部分参考
《Spring源码深度解析》
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正