文章目录
一、前言
本文是 Spring源码分析:Spring源码分析七:BeanFactoryPostProcessor 的处理 - invokeBeanFactoryPostProcessors 的衍生文章。主要是因为本人菜鸡,在分析源码的过程中还有一些其他的内容不理解,故开设衍生篇来完善内容以学习。
ConfigurationClassPostProcessor 的分析受篇幅所限,分为上下两篇
上篇 分析 postProcessBeanDefinitionRegistry 方法的调用。
下篇 分析 postProcessBeanFactory 方法的调用。
ConfigurationClassPostProcessor
是非常重要的一个 后处理器。 ConfigurationClassPostProcessor
完成了 配置类的解析和保存。将所有需要注入的bean解析成 BeanDefinition保存到 BeanFactory 中。
1. ConfigurationClassPostProcessor
首先来讲解一下 ConfigurationClassPostProcessor
的结构图如下。
可见ConfigurationClassPostProcessor
接口实现了BeanDefinitionRegistryPostProcessor
(BeanFactory 的后处理器)
PriorityOrdered
(设置自己的优先级为最高) 和各种 Aware 接口。
我们这里重点看的是 BeanDefinitionRegistryPostProcessor
接口的两个方法:
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
关于这两个方法的调用时机和作用,我们在之前的文章已经讲过,这里不再赘述。
二 、正文
上面已经提及,本文主要关注 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
方法的解析。所以我们下面来看看 postProcessBeanDefinitionRegistry
方法
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
.... 省略部分代码
// 关键方法,解析 配置类的定义
processConfigBeanDefinitions(registry);
}
可以看到 postProcessBeanDefinitionRegistry
方法中并没有处理什么逻辑,真正逻辑在其调用的 processConfigBeanDefinitions
方法中
1. processConfigBeanDefinitions
processConfigBeanDefinitions
方法完成了关于配置类的所有解析。
详细代码如下
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 获取已经解析的BeanName
String[] candidateNames = registry.getBeanDefinitionNames();
// 遍历BeanName
for (String beanName : candidateNames) {
// 获取BeanDefinition
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 如果bean被解析过,if 属性成立,这里是为了防止重复解析
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
// ConfigurationClassUtils.checkConfigurationClassCandidate 解析了当前bean是否是配置类,关于其详细内容,后面解析
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);
});
... 忽略部分代码
// 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 {
// 开始解析。后面详细分析
parser.parse(candidates);
// 这里的校验是校验如果是被 @Configuration修饰且proxyBeanMethods属性为true,则类不能为final。如果@Bean修饰的方法,则必须是可覆盖的.
// 因为@Configuration(proxyBeanMethods = true) 是需要cglib代理的,所以不能为终态, @Bean也一样
// 是否需要代码是根据 类或方法上的 @Scope注解指定的,默认都是不代理
parser.validate();
// configClasses 保存这次解析出的配置类
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());
}
// 注册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
在 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修饰的方法。
下面我们来看具体代码:
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
// 获取className
String className = beanDef.getBeanClassName();
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
... 忽略部分代码
// 获取bean上的Configuration 注解的属性。如果没有被 @Configuration 修饰 config 则为null
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
// 如果被 @Configuration 修饰 && proxyBeanMethods 属性为 true。 @Configuration 的 proxyBeanMethods 属性默认值即为 true。
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
// 设置 CONFIGURATION_CLASS_ATTRIBUTE 为 full
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
// 如果被 @Configuration 修饰 && isConfigurationCandidate(metadata) = true
else if (config != null || isConfigurationCandidate(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.
// 按照@Order 注解排序
Integer order = getOrder(metadata);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
}
1.1.1. isConfigurationCandidate
在上面的代码中,我们看到 判断是否是 Lite
的关键方法是 isConfigurationCandidate
。其代码如下:
// candidateIndicators 的定义
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());
}
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
// 不能是接口
if (metadata.isInterface()) {
return false;
}
// Any of the typical annotations found?
// 被 candidateIndicators 中的注解修饰
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);
上面解析了如何判断一个类是否是配置类。也完成了配置类的筛选。那么开始进行配置类的解析,在 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) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
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);
}
}
this.deferredImportSelectorHandler.process();
}
里面的 parse 方法会调用 processConfigurationClass
方法,所以我们直接进入 processConfigurationClass
方法:
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
// 判断是否应该跳过当前类的解析。这里面解析了 @Conditional 注解
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// 判断是否已经解析过。configurationClasses 中保存着已经解析过的配置类
// 这里应该是 注入的配置类优先级高于引入的配置类
// 如果配置类被多次引入则合并属性
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
// 如果当前配置类和之前解析过的配置类都是引入的,则直接合并
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, filter);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
// 保存解析过的 配置类
this.configurationClasses.put(configClass, configClass);
}
额外提一句,this.conditionEvaluator.shouldSkip
中对 @Conditional
注解 注解进行了处理,由于篇幅所限,这里不再展开叙述。
看了这么久的源码,也知道了Spring的套路,方法名以do开头的才是真正做事的方法, 所以我们来看 doProcessConfigurationClass
方法。
@Nullable
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// 1. 处理 @Component 注解
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass, filter);
}
// 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.info("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), filter, 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 注解
这里对 @Component
的处理其实是处理配置类的内部类,即如果当前类是被 @Component
修饰,则需要判断其内部类是否需要解析。
// 首先判断如果配置类被@Component 修饰,则调用processMemberClasses 方法处理
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass, filter);
}
processMemberClasses
方法的代码如下:
代码逻辑也很简单。即如果配置类中有内部类,则判断其内部类是否是配置类,如果是则递归去解析新发现的内部配置类。
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
Predicate<String> filter) 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), filter);
}
finally {
// 解析完毕出栈
this.importStack.pop();
}
}
}
}
}
注:
- 判断内部类是否是配置类,这里是检测内部类是否满足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
注解属性 :
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 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
指定自动扫描的路径。
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
// 如果当前配置类被 @componentScans 或 @componentScan 注解修饰
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
// 遍历扫描出来的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中发现了新的配置类,则递归去解析。
1.2.4 处理 @Import、ImportSelector、 ImportBeanDefinitionRegistrar
这里可以看到,该方法处理的包括 @Import、ImportSelector、 ImportBeanDefinitionRegistrar。
这三个注解或接口都可以完成Bean的引入功能。
我们直接来看 processImports 方法,注释都比较清楚 :
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
// 如果 没有需要引入的直接返回
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 = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
// 调用 selectImports 方法获取需要引入的类,并递归再次处理。
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 递归解析
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, 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 =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
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), exclusionFilter);
}
}
}
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 中
// Process any @ImportResource annotations
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 处理接口默认方法
这里也是将接口的非抽象接口保存到 configClass 中
/**
* Register default methods on interfaces implemented by the configuration class.
*/
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 this.reader.loadBeanDefinitions(configClasses);
上面也说了,在parser.parse(candidates);
方法中,将各种注解的属性值都解析了处来,并保存到了 configClass
的各种属性中。
而在 this.reader.loadBeanDefinitions(configClasses);
中才真正处理了这些属性。所以我们接下来看看loadBeanDefinitions
的处理流程。
loadBeanDefinitions
遍历了每一个ConfigurationClass
,通过loadBeanDefinitionsForConfigurationClass
方法处理。
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
所以我们来看看 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;
}
// 如果配置是被引入的
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 遍历配置类中的所有方法
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// 加载 通过 @ImportResource 的 获取的bean
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 加载 通过 @Import 的 获取的bean
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
下面 我们来详细看看每个方法。
1.3.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.isTraceEnabled()) {
logger.trace("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
方法如下
protected Class<? extends Annotation> scopeAnnotationType = Scope.class;
@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.3.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 方法和非静态
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
beanDef.setFactoryBeanName(configClass.getBeanName());
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
修饰的方法创建出一个 ConfigurationClassBeanDefinition
注册到 Spring容器中。在后面的Bean实例化过程中。会在 AbstractAutowireCapableBeanFactory#createBeanInstance
方法中有如下两句。
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
而@Bean
的修饰的方法会调用instantiateUsingFactoryMethod
方法,通过反射调用方法,并将反射结果注入到Spring容器中,完成 @Bean
注解的功能。
1.3.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.3.4 loadBeanDefinitionsFromRegistrars
loadBeanDefinitionsFromRegistrars
方法注册了了@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注解。具体请看:Spring源码分析衍生篇五:AutowiredAnnotationBeanPostProcessor简单来说
ConfigurationClassPostProcessor
完成了 Bean的扫描与解析,AutowiredAnnotationBeanPostProcessor
完成了Bean 属性的注入
本文只分析了 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
方法,对于ConfigurationClassPostProcessor#postProcessBeanFactory
方法的分析则放在后篇,这里简单的说明 ConfigurationClassPostProcessor#postProcessBeanFactory
方法通过cglib动态代理,完成了 对 @Bean修饰方法的代理,以确保其正确语义。
以上:内容部分参考
《Spring源码深度解析》
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正