今天在使用@ConditionalOnBean注解的时候,条件只有一个自定义注解,一直无法匹配成功,然后开始调试查看问题出现在哪儿,最终得到结果,@ConditionalOnBean在使用条件只有注解的时候,必须要指定type或者name才可以,所以最终我写了个必然会成功的types将问题解决,
/**
* 启用XXX.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface EnableXXX {
}
/**
* 基础配置.
*/
@Configuration
public class XXXConfig {
// 设定type为XXXConfig,这样条件会永远成立,问题解决
@Bean
@ConditionalOnBean(type = "XXXConfig ", annotation = EnableXXX.class)
public LoggerConfiguration loggerConfiguration() {
return new LoggerConfiguration();
}
}
@SpringBootApplication
@EnableXXX
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
下列代码为找原因的过程
// 下列代码是我当时遇到问题的代码的大致结构
/**
* 启用XXX.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface EnableXXX {
}
/**
* 基础配置.
*/
@Configuration
public class XXXConfig {
@Bean
@ConditionalOnBean(annotation = EnableXXX.class)
public LoggerConfiguration loggerConfiguration() {
return new LoggerConfiguration();
}
}
@SpringBootApplication
@EnableXXX
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
// 下列代码为找原因的过程
// 在OnBeanCondition类的getMatchOutcome有这么一段代码
if (metadata.isAnnotated(ConditionalOnBean.class.getName())) {
// 在此处会去实例化BeanSearchSpec,我们去看看实例化代码
BeanSearchSpec spec = new BeanSearchSpec(context, metadata, ConditionalOnBean.class);
MatchResult matchResult = getMatchingBeans(context, spec);
if (!matchResult.isAllMatched()) {
String reason = createOnBeanNoMatchReason(matchResult);
return ConditionOutcome.noMatch(ConditionMessage
.forCondition(ConditionalOnBean.class, spec).because(reason));
}
matchMessage = matchMessage.andCondition(ConditionalOnBean.class, spec)
.found("bean", "beans")
.items(Style.QUOTE, matchResult.getNamesOfAllMatches());
}
// 调用此构造进行实例化
public BeanSearchSpec(ConditionContext context, AnnotatedTypeMetadata metadata,
Class<?> annotationType) {
// 调用此构造进行实例化
this(context, metadata, annotationType, null);
}
// 最终的构造方法
public BeanSearchSpec(ConditionContext context, AnnotatedTypeMetadata metadata,
Class<?> annotationType, Class<?> genericContainer) {
this.annotationType = annotationType;
MultiValueMap<String, Object> attributes = metadata
.getAllAnnotationAttributes(annotationType.getName(), true);
collect(attributes, "name", this.names);
collect(attributes, "value", this.types);
collect(attributes, "type", this.types);
collect(attributes, "annotation", this.annotations);
collect(attributes, "ignored", this.ignoredTypes);
collect(attributes, "ignoredType", this.ignoredTypes);
collect(attributes, "parameterizedContainer", this.parameterizedContainers);
this.strategy = (SearchStrategy) attributes.getFirst("search");
BeanTypeDeductionException deductionException = null;
try {
// 在这个地方,我们可以知道,我并没有设定types和names,那么spring会很贴心当帮我做个类型推断
if (this.types.isEmpty() && this.names.isEmpty()) {
addDeducedBeanType(context, metadata, this.types);
}
}
catch (BeanTypeDeductionException ex) {
deductionException = ex;
}
validate(deductionException);
}
// 开始帮我做类型推断,调用addDeducedBeanTypeForBeanMethod方法
private void addDeducedBeanType(ConditionContext context, AnnotatedTypeMetadata metadata, final List<String> beanTypes) {
if (metadata instanceof MethodMetadata && metadata.isAnnotated(Bean.class.getName())) {
addDeducedBeanTypeForBeanMethod(context, (MethodMetadata) metadata, beanTypes);
}
}
private void addDeducedBeanTypeForBeanMethod(ConditionContext context,
MethodMetadata metadata, final List<String> beanTypes) {
try {
// 在此处将我的方法的返回值设定上去,导致我的beanTypes变为了我方法的返回值
// 这个时候types就会变为我的返回值也就是LoggerConfiguration,好的 我们继续往下看
Class<?> returnType = getReturnType(context, metadata);
beanTypes.add(returnType.getName());
}
catch (Throwable ex) {
throw new BeanTypeDeductionException(metadata.getDeclaringClassName(),
metadata.getMethodName(), ex);
}
}
// 开始做匹配判断
protected final MatchResult getMatchingBeans(ConditionContext context,
BeanSearchSpec beans) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
if (beans.getStrategy() == SearchStrategy.ANCESTORS) {
BeanFactory parent = beanFactory.getParentBeanFactory();
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent,
"Unable to use SearchStrategy.PARENTS");
beanFactory = (ConfigurableListableBeanFactory) parent;
}
MatchResult matchResult = new MatchResult();
boolean considerHierarchy = beans.getStrategy() != SearchStrategy.CURRENT;
TypeExtractor typeExtractor = beans.getTypeExtractor(context.getClassLoader());
List<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(
beans.getIgnoredTypes(), typeExtractor, beanFactory, context,
considerHierarchy);
// 在上面我们得知,types当前为我的方法返回值 也就是LoggerConfiguration
// 这个时候我想创建LoggerConfiguration,但是呢,条件为LoggerConfiguration必须在Bean工厂中存在
// 所以就导致我无法成功创建出来LoggerConfiguration
for (String type : beans.getTypes()) {
Collection<String> typeMatches = getBeanNamesForType(beanFactory, type,
typeExtractor, context.getClassLoader(), considerHierarchy);
typeMatches.removeAll(beansIgnoredByType);
if (typeMatches.isEmpty()) {
matchResult.recordUnmatchedType(type);
}
else {
matchResult.recordMatchedType(type, typeMatches);
}
}
for (String annotation : beans.getAnnotations()) {
List<String> annotationMatches = Arrays
.asList(getBeanNamesForAnnotation(beanFactory, annotation,
context.getClassLoader(), considerHierarchy));
annotationMatches.removeAll(beansIgnoredByType);
if (annotationMatches.isEmpty()) {
matchResult.recordUnmatchedAnnotation(annotation);
}
else {
matchResult.recordMatchedAnnotation(annotation, annotationMatches);
}
}
for (String beanName : beans.getNames()) {
if (!beansIgnoredByType.contains(beanName)
&& containsBean(beanFactory, beanName, considerHierarchy)) {
matchResult.recordMatchedName(beanName);
}
else {
matchResult.recordUnmatchedName(beanName);
}
}
return matchResult;
}