文章目录
前言
本篇文章将解析ConfigurationClassPostProcessor这个类解析配置注解时都干了什么。其中会分析以下几点问题:
- @Bean注册Bean到容器中
- @Import导入处理
- @Conditional条件解析
- @ComponentScan注解注册Bean到容器中
- @Configuration配置类的完全模式和轻量模式
- …还有其他配置注解的解析,本篇文章不作分析,只对以上论点进行重点分析。相信读懂本篇文章的读者有能力自主分析其他的配置解析过程
所以,我们将以上几个论点当作问题,带着问题来分析ConfigurationClassPostProcessor这个类。
BeanFactoryPostProcessor处理
首先,ConfigurationClassPostProcessor是一个BeanFactoryPostProcessor,所以它具有在Bean定义阶段动态修改和添加Bean的定义的能力。而处理解析配置注解的开始,正是从postProcessBeanDefinitionRegistry方法开始的。
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// ...
processConfigBeanDefinitions(registry);
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// 即将被解析的Bean名单
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 拿到目前所有的Bean名称
// 需要注意的是,目前你定义的Bean或许只有一个,就是引导类那个Configuration(如果是springboot的话)
// 因为在引导类你将其显式的注册进了Spring上下文
// 现在还没进行@Bean、@ComponentScan、自动装配之类的注册Bean
// 所以经过接下来的操作,将动态注册一堆Bean上去
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
// 变为Bean定义对象
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 这里是重复判断,因为接下来判断如果是配置类需要被解析,会在Bean定义中增加属性
// 这里就根据Bean定义查找是否有对应属性,如果有表示已经处理过了
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
// 判断是否是配置类
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
// 这里看出,解析的时候是有序的
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
// 最主要的解析类,由它负责解析以上获得的Bean名单
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 {
// 解析工作
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// ...
// 将解析到的类注册进IOC容器中
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
// ...
}
while (!candidates.isEmpty());
// ...
}
我这里省略了一些代码,只看主干部分。
判断是否需要被解析
首先看一下判断是否是配置类的逻辑
ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)
如果判断是配置类,才会继续往下解析,所以这里看一下其是如何判断的
if (isFullConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
else if (isLiteConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
Full完全模式与Lite轻量模式
这里省略了一些获取注解信息的过程,主要是isFullConfigurationCandidate和isLiteConfigurationCandidate这两个方法进行判断的
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
return metadata.isAnnotated(Configuration.class.getName());
}
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 isLiteConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
if (metadata.isInterface()) {
return false;
}
// Any of the typical annotations found?
// 此集合为上面所述的那几个注解
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
// Finally, let's look for @Bean methods...
try {
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;
}
}
从这里可以看出来,有两种配置解析模式:
-
如果类被打上@Configuration,此为完全解析模式(Full)
-
如果此类带有以上4个注解或者有存在方法被打上@Bean注解的,都算配置类,都需要被解析,此为轻量解析模式(Lite)
那么有什么区别呢?完全解析模式会使用动态代理的方式将配置类进行动态代理,可以有如下操作
@Bean
public Test test(){
Test test = new Test();
test.setUser(user());
return test;
}
@Bean
public User user(){
return new User("xx");
}
Test Bean的依赖User对象,直接调用方法名即可,会将其注入Spring中的Bean,如果是lite模式,就不会注入Spring中的Bean,而是新new一个对象,我们可以试验一下
@EnableAutoConfiguration
public class TestAutoConfigure {
public static void main(String[] args