1.在源码中,在生成beanfinition中有有如一段代码
以下代码逻辑中执行this.conditionEvaluator.shouldSkip返回true直接跳出beandefinition生成逻辑
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
@Nullable BeanDefinitionCustomizer[] customizers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
abd.setInstanceSupplier(supplier);
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
if (customizers != null) {
for (BeanDefinitionCustomizer customizer : customizers) {
customizer.customize(abd);
}
}
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
2.查看shouldSkip代码逻辑
1). 检查class中是否有@Condition注解,没有直接return fase
2). 命出@Condition里内容,直接调用其matches方法,不满足返回true
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
return false;
}
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<>();
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
Condition condition = getCondition(conditionClass, this.context.getClassLoader());
conditions.add(condition);
}
}
AnnotationAwareOrderComparator.sort(conditions);
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;
}
3.从1和2我们可以得到结论
关键在于@Condtion注解中内容的matches方法如果满足才可以继续生成bean
4.@Condition注解代码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* All {@link Condition} classes that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}
从以下代码可以@Condition中内容其实就是Condition接口实现
public interface Condition { boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }
5.运用
比如我们在生成类的beandefinition时查看类上有没有@EnableMyImportClass 注解,
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MyDeferredImportSelector.class)
public @interface EnableMyImportClass {
String[] value();
}
没有不让其生成,我们自定义的MyCondition类如下:
public class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MergedAnnotations mergedAnnotations= metadata.getAnnotations();
return mergedAnnotations.isDirectlyPresent(EnableMyImportClass.class);
}
}
然后在某个类加上这样注解来测试
@Configuration
@PropertySource(value = "classpath:aa.properties")
@EnableMyImportClass({"testme"})
@Conditional(MyCondition.class)
public class AppConfig {
如果我们这里将注解EnableMyImportClass去掉,测试后会发现AppConfig的beandefintion根据就没有生成,如下
如果没有去掉正常情况如下:
6结论
从以上的内容可以看出@Condition一个非常实用的注解,可以根据需求在做一些非常定制化的产品加载bean的逻辑,比如在有消息中间件产品中,可以根据系统参数据来判断是否启用对应MQ bean.