一、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 {}
三、CCPP
的postProcessBeanDefinitionRegistry
方法解析
postProcessBeanDefinitionRegistry
主要用来向容器中注册新的BeanDefinition
下面是CCPP
的postProcessBeanDefinitionRegistry
方法的的代码:
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;
}
下面是对配置类的具体处理代码,主要流程如下:
具体处理流程:
- 对@Component注解的处理;
- 对@PropertySource注解的处理;
- 对@ComponentScan注解的处理;
- 对@Import注解的处理;
- 对@ImportResource注解的处理;
- 对@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的处理主要分为两步:
- 解析扫描得到ComponentScan注解中指定的包路径下所有符合条件的类所对应的BeanDefinition并注册到IOC容器中;
- 这里的解析流程和
@Component
注解修饰的类转化为BeanDefinition的流程类似- 对得到的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的处理:
- 首先获取到注解中指定的要导入的资源(这里可以导入的资源上面也有
@Import
注解,所以这里会涉及到递归获取操作)- 对第一步获取到的资源进行处理(SpringBoot时在具体分析)
对@Bean注解的处理
对
@Bean
的处理主要分为两步:
- 获取到当前类中所有的加了@Bean注解的方法;
- 将第一步获取到的方法加到当前配置类中
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}