@ConditionalOnBean在方法上失效
在自定义一个日志切面,作为一个工具提供给其它的模块依赖时,发现@ConditionalBean写在方法上失效了,却写在类上注入成功的问题。于是通过源码打断点,发现@ConditionalOnBean在方法上时,对自定义
的bean无效且会导致bean注入失败。
//@ConditionalOnBean(annotation = {EnableAutoPintLog.class}) 此处生效
public class LogAutoConfiguration {
@Bean
@ConditionalOnBean(annotation = {EnableAutoPintLog.class}) //此处会失败
public LogAspect logAspect() {
return new LogAspect();
}
}
首先全局搜索ConfigurationClassPostProcessor
类,找到下面的postProcessBeanDefinitionRegistry
入口,在下图所示。
再断点进行,在this.reader.loadBeanDefinitions(configClasses)
打上断点
断点再到如下图所示
在断点处加上判断表达式configClass.getMetadata().getClassName().equals("com.zh.common.util.config.LogAutoConfiguration")
,让停在指定的bean,并进入该方法。由于bean写在方法上,所以loadBeanDefinitionsForBeanMethod(beanMethod)
处打上断点
进去,到this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)
在shouldSkip
方法中,判断该bean是否带有注解@ConditionalOnBean
,当方法带有才会进入下面的condition.match
逻辑。
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
return false;
}
... 省略代码
if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
return true;
}
}
return false;
}
进入matchs方法,再进入getMatchOutcome方法
@Override
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String classOrMethodName = getClassOrMethodName(metadata);
try {
// 此处进入
ConditionOutcome outcome = getMatchOutcome(context, metadata);
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConditionMessage matchMessage = ConditionMessage.empty();
MergedAnnotations annotations = metadata.getAnnotations();
if (annotations.isPresent(ConditionalOnBean.class)) {
// 初始化,重要
Spec<ConditionalOnBean> spec = new Spec<>(context, metadata, annotations, ConditionalOnBean.class);
// 自定义bean时候,会找不到对应的type而导致match失败
MatchResult matchResult = getMatchingBeans(context, spec);
if (!matchResult.isAllMatched()) {
String reason = createOnBeanNoMatchReason(matchResult);
return ConditionOutcome.noMatch(spec.message().because(reason));
}
matchMessage = spec.message(matchMessage).found("bean", "beans").items(Style.QUOTE,
matchResult.getNamesOfAllMatches());
}
!!!此处重要,进入new spec构造方法里面,在deducedBeanType处推断bean的类型并赋值给type。以至后面的由于自定义bean找不到type,而判断类型出错
先判断是不是通过@bean在方法上注入的bean,是的话推断它的类型type并返回赋值,此时会导致后面因为这里不为空且找不到指定的type而match不成功。
当在类上添加注解@ConditionalOnBean上时候,由于不满足条件,返回空集。而不会导致type找不到问题。
private Set<String> deducedBeanType(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (metadata instanceof MethodMetadata && metadata.isAnnotated(Bean.class.getName())) {
return deducedBeanTypeForBeanMethod(context, (MethodMetadata) metadata);
}
return Collections.emptySet();
}
由于是自定义的bean,最终匹配不成功,忽略不处理,注入失败。