ConfigurationClassPostProcessor分析

一、ConfigurationClassPostProcessor介绍

首先看下ConfigurationClassPostProcessor(CCPP)的类图:
在这里插入图片描述

可以看到CCPP是一个BeanDefinitionRegistryPostProcessor(BDRPP),而BDRPP的主要功能就是用来向容器中注册新的BeanDefinition,所以CCPP的作用很可能也是在某种场景下注册BeanDefinition到Spring的容器中。
类的介绍信息:
在这里插入图片描述

可以看到:
1、CCPP是用来处理被@Configuration修饰的类,并将类中加了@Bean注解的方法解析得到它们相对应的BeanDefinition并注册到容器中(其实这里不仅仅是处理@Configuration修饰的类)。
2、CCPP默认注册时机是在配置文件中开启注解支持或者开启包扫描,就会注册CCPP到容器中。
看下开启包扫描时何时注册CCPP的:

public BeanDefinition parse(Element element, ParserContext parserContext) {
    // 获取要扫描的包
    String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
    basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
    String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
            ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
    // 创建扫描器
    ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    // 进行扫描并将得到BeanDefinition注册到容器中
    Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
    // 发送注册时间,这里会注册CCPP
    registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
    return null;
}

在包扫描结束并将BeanDefinition注册到容器中之后,会调用registerComponents方法,在这个方法中会注册CCPP。

protected void registerComponents(XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {
		// ......
		if (annotationConfig) {
			Set<BeanDefinitionHolder> processorDefinitions =
                    // 这里会注册CCPP
					AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
			// ......
		}
        // ......
	}
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {
		// ......
    
        // 这是beanName:org.springframework.context.annotation.internalConfigurationAnnotationProcessor
		if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            // ==============================================
            // 注册ConfigurationClassPostProcessor
            // ==============================================
			RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
		}
    	// org.springframework.context.annotation.internalAutowiredAnnotationProcessor
		if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            // 注册AutowiredAnnotationBeanPostProcessor
			RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		// org.springframework.context.annotation.internalCommonAnnotationProcessor
		if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            // 注册CommonAnnotationBeanPostProcessor
			RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		// org.springframework.context.annotation.internalPersistenceAnnotationProcessor
		if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            // PersistenceAnnotationBeanPostProcessor
			RootBeanDefinition def = new RootBeanDefinition();
			try {
				def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
						AnnotationConfigUtils.class.getClassLoader()));
			}
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
		}
        // ......
	}

可以看到,这里是向容器中注册了CCPP,而CCPP是一个BDRPP,所以在Spring执行BFPP时,会调用执行CCPP中的postProcessBeanDefinitionRegistry方法向容器中注册BeanDefinition。

二、@Configuration修饰的类是何时解析成BeanDefinition注册到容器中的?

通过查看@Configuration的定义信息可知:@Configuration@Component所修饰,也就是说@Configuration拥有元注解@Component,在Spring解析到<context:component-scan>标签时就会把被@Configuration修饰的类解析成BeanDefinition并注册到IOC容器中

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 被@Component修饰 
@Component
public @interface Configuration {}

三、CCPPpostProcessBeanDefinitionRegistry方法解析

postProcessBeanDefinitionRegistry主要用来向容器中注册新的BeanDefinition

下面是CCPPpostProcessBeanDefinitionRegistry方法的的代码:

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    // ......
    processConfigBeanDefinitions(registry);
}

主要流程:

1、获取到容器中所有加了@Configuration注解的类所对应的BeanDefinition

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
	// 获取到截止目前为止已经注册到Spring容器中的所有BeanDefinition的beanName
    String[] candidateNames = registry.getBeanDefinitionNames();

	// 遍历所有的beanName,判断是否是当前BDRPP要处理的BeanDefinition,其实就是加了@Configuration的类所对应的BeanDefinition
    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        // 有某个属性就说明已经被作为一个配置类处理过,无需重复处理
        // ConfigurationClassPostProcessor.configurationClass
        if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
            }
        }
        // 校验是否为配置类的BeanDefinition
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }

    // ......
    // 排序
    // ......

    // 创建一个配置类的解析器
    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);
        // ......
    }
    while (!candidates.isEmpty());

    // ......
}

下面是校验是否为配置类的代码:其实就是判断类上是否加了**@Configuration**注解

public static boolean checkConfigurationClassCandidate(
        BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
    // ......
    AnnotationMetadata metadata;
    // ......获取到注解元数据

    // 获取@Configuration中各个属性的信息,返回值为null则说明没有加@Configuration注解,不会成为候选者
    Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());

    // 下面这段代码是处理@Configuration的proxyBeanMethods属性
    if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
        // full
        beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
    }
    // 被@Configuration修饰,或者被@Component或@ComponentScan或@Import或@ImportResource修饰,或者拥有被@Bean修饰的方法的类
    // 都会成为配置类的候选者
    else if (config != null || isConfigurationCandidate(metadata)) {
        // lite
        beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
    }
    else {
        return false;
    }

    // 处理排序
    Integer order = getOrder(metadata);
    if (order != null) {
        beanDef.setAttribute(ORDER_ATTRIBUTE, order);
    }
    return true;
}

下面这个方法使用了判断类上是否被@Component@ComponentScan@Import@ImportResource修饰,或者拥有被@Bean修饰的方法

public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
    // Do not consider an interface or an annotation...
    if (metadata.isInterface()) {
        return false;
    }

    // static {
	// 	candidateIndicators.add(Component.class.getName());
	// 	candidateIndicators.add(ComponentScan.class.getName());
	// 	candidateIndicators.add(Import.class.getName());
	// 	candidateIndicators.add(ImportResource.class.getName());
	// }
	// 
	// 被@Configuration修饰,且被@Component或@ComponentScan或@Import或@ImportResource修饰
    for (String indicator : candidateIndicators) {
        if (metadata.isAnnotated(indicator)) {
            return true;
        }
    }
    // 拥有被@Bean修饰的方法
    try {
        return metadata.hasAnnotatedMethods(Bean.class.getName());
    }
    // ......
}

下面是parser.parse(candidates)方法的具体流程解析:

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            if (bd instanceof AnnotatedBeanDefinition) {
                // 加了注解的BeanDefinition都是AnnotatedBeanDefinition,所以会走这个parse方法
                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());
            }
        }
        // ......
    }
    this.deferredImportSelectorHandler.process();
}
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
    // 这里是对@Conditional注解的处理,如果类上加了@Conditional注解,且不满足条件会直接返回,不会再走后续流程
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
        return;
    }

    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);
}

下面是对@Conditional注解的处理流程:

public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
    // 类上没有加@Conditional注解,直接返回false
    if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
        return false;
    }
	// phase为ConfigurationPhase.PARSE_CONFIGURATION,不为null,不会走到这里
    if (phase == null) {
        if (metadata instanceof AnnotationMetadata &&
                ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
            return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
        }
        return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
    }

    List<Condition> conditions = new ArrayList<>();
    // 得到所有的Condition的实例对象
    // getConditionClasses:获取到@Conditional中指定的所有Condition的Class的全路径
    for (String[] conditionClasses : getConditionClasses(metadata)) {
        // 实例化所有的Condition
        for (String conditionClass : conditionClasses) {
            Condition condition = getCondition(conditionClass, this.context.getClassLoader());
            conditions.add(condition);
        }
    }

    // 对每一个Condition进行处理,返回结果主要由condition.matches这个方法来决定
    for (Condition condition : conditions) {
        ConfigurationPhase requiredPhase = null;
        if (condition instanceof ConfigurationCondition) {
            requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
        }
        if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
            return true;
        }
    }

    return false;
}

下面是对配置类的具体处理代码,主要流程如下:

具体处理流程:

  1. 对@Component注解的处理;
  2. 对@PropertySource注解的处理;
  3. 对@ComponentScan注解的处理;
  4. 对@Import注解的处理;
  5. 对@ImportResource注解的处理;
  6. 对@Bean注解的处理;
protected final SourceClass doProcessConfigurationClass(
        ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
        throws IOException {

    // 对加了@Component注解的配置类进行处理
    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
        // Recursively process any member (nested) classes first
        processMemberClasses(configClass, sourceClass, filter);
    }

    // 对加了@PropertySource注解的配置类进行处理
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), PropertySources.class,
            org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
            processPropertySource(propertySource);
        }
    }

    // 对加了@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());
                }
            }
        }
    }
    
    // 对加了@Import注解的配置类进行处理
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

    // 对加了@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);
        }
    }

    // 处理加了@Bean注解的方法
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

    // 处理接口中的默认方法
    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();
        }
    }

    return null;
}

对@Component注解的处理

对@Component注解的处理,主要是对其内部类进行处理,类似于下面这种:

@Configuration
@Conditional(value = CarCondition.class)
public class Car {
	private String brand;
	
	@Component
	class DetailCar {
		
	}
}

在上面的代码中,类Car在Spring加载XML配置文件时就已经解析成BeanDefinition,但内部类DetailCar并未解析,所以这里需要对其进行解析。

MembeClass就是内部类的意思,下面是处理内部类的方法,可以看到它的处理逻辑和CCPP的postProcessBeanDefinitionResigtry是类似的,调用的方法和CCPP的相同

在这里插入图片描述

对@PropertySource注解的处理

这里对@PropertySource的处理主要是根据注解中指定的资源路径,用Resource类型的对象表示资源并将资源加载到Spring容器中。

private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
    // 解析注解中的name属性
    String name = propertySource.getString("name");
    if (!StringUtils.hasLength(name)) {
        name = null;
    }
    // 解析注解中的encoding属性
    String encoding = propertySource.getString("encoding");
    if (!StringUtils.hasLength(encoding)) {
        encoding = null;
    }
    // 解析注解中的value属性,从这里就可以获取到资源的具体路径
    String[] locations = propertySource.getStringArray("value");
    // ......
    for (String location : locations) {
        try {
            String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
            // 加载资源
            Resource resource = this.resourceLoader.getResource(resolvedLocation);
            // 将资源注册到容器中
            addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
        }
        // ......
    }
}

对@ComponentScan注解的处理

对@ComponentScan的处理主要分为两步:

  1. 解析扫描得到ComponentScan注解中指定的包路径下所有符合条件的类所对应的BeanDefinition并注册到IOC容器中;
    1. 这里的解析流程和@Component注解修饰的类转化为BeanDefinition的流程类似
  2. 对得到的BeanDefinition判断是否是配置类,并进行解析(和CCPP的postProcessBeanDefinitionRegistry类似)
// 扫描得到指定包路径下符合条件的BeanDefinition
Set<BeanDefinitionHolder> scannedBeanDefinitions =
        this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
    // 校验是否是配置类的候选者,是则进行进一步的解析
    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
        parse(bdCand.getBeanClassName(), holder.getBeanName());
    }
}

对@Import注解的处理

对@Import的处理:

  1. 首先获取到注解中指定的要导入的资源(这里可以导入的资源上面也有@Import注解,所以这里会涉及到递归获取操作)
  2. 对第一步获取到的资源进行处理(SpringBoot时在具体分析)

对@Bean注解的处理

@Bean的处理主要分为两步:

  1. 获取到当前类中所有的加了@Bean注解的方法;
  2. 将第一步获取到的方法加到当前配置类中
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
    configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值