本文基于Spring 5.2.7
在ConfigurationClassPostProcessor的解析当中,有很多关于Configuration的方法,我们没有作过多解释,这里将他们的源码做个解释。
org.springframework.context.annotation.ConfigurationClassUtils#checkConfigurationClassCandidate()
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
String className = beanDef.getBeanClassName();
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
AnnotationMetadata metadata;
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
// Can reuse the pre-parsed metadata from the given BeanDefinition...
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.
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
BeanPostProcessor.class.isAssignableFrom(beanClass) ||
AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
EventListenerFactory.class.isAssignableFrom(beanClass)) {
return false;
}
metadata = AnnotationMetadata.introspect(beanClass);
}
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;
}
}
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
else if (config != null || isConfigurationCandidate(metadata)) {
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);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
}
上面一大半的代码都是在获取org.springframework.core.type.AnnotationMetadata,这个对象存储了对象上注解的信息,所以要先获取到他。对于AnnotationMetadata,我们会专门开一篇文章讲解。
42行开始的这个if else判断可能不是那么好理解,要自己琢磨下,
第一种case是有@Configuration注解且proxyBeanMethods属性为true,这种就是full模式
第二种case是有@Configuration注解或者isConfigurationCandidate(),这种就是lite模式
第三种case是剩下的,剩下的不是配置类
编程中,由于if else有执行的先后顺序关系,所以条件放在不同时序地方执行,表现出的逻辑是不一样的,比如第二个case中,只判断了config != null,由于有第一个case逻辑的原因,能到第二个case,就说明第二个config != null的实际含义是conifg != null && proxyBeanMethods=false,作者的写法很简洁,利用了上面语义上的逻辑做了隐式的条件,这个大家可以借鉴,可以简化条件书写。
所以这段代码的逻辑是非常清晰的,判断是否是配置类,如果是full模式或者lite模式,那么就是配置类,如果2者都不是的话就不是配置类。
其中判断是lite模式配置的方法也是我们需要关注的:
org.springframework.context.annotation.ConfigurationClassUtils#isConfigurationCandidate()
这段逻辑和我们给lite模式下的一定如出一辙。
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?
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;
}
}