Spring @Conditional指定条件匹配
1.指示只有在所有指定条件匹配时,组件才有资格注册。
2.条件是在注册bean定义之前可以通过编程确定的任何状态。
@Conditional注释可以以以下任何方式使用:
- 作为任何直接或间接用@Component注释的类的类型级注释,包括@Configuration类
- 作为元注释,用于编写自定义的构造型注释
- 作为任何@Bean方法上的方法级注释
- 如果@Configuration类用@Conditional标记,则与该类关联的所有@Bean方法、@Import 和@ComponentScan annotations都将受到这些条件的约束。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* 要注册组件,必须匹配的所有Condition类。
*/
Class<? extends Condition>[] value();
}
Condition 为了注册组件,必须匹配的单个条件。
在注册bean定义之前立即检查条件,并且可以根据此时可以确定的任何标准自由否决注册。
条件必须遵循与BeanFactoryPostProcessor相同的限制,并注意永远不要与bean实例交互。为了对与{@Configuration} beans交互的条件进行更细粒度的控制,请考虑实现ConfigurationCondition接口。
@FunctionalInterface
public interface Condition {
/**
*确定条件是否匹配
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
当与@Configuration一起使用时,提供更细粒度控制的Condition。允许某些条件在匹配时根据配置阶段进行调整。例如,检查bean是否已经注册的条件可能选择仅在REGISTER_bean ConfigurationCondition.ConfigurationPhase期间进行评估。
public interface ConfigurationCondition extends Condition {
/**
* 返回应在其中评估条件的ConfigurationCondition.ConfigurationPhase
*/
ConfigurationPhase getConfigurationPhase();
/**
* .可以评估条件的各种配置阶段
*/
enum ConfigurationPhase {
/**
* 应在解析@Configuration类时评估Condition。
* 如果此时条件不匹配,则不会添加@Configuration类。
*/
PARSE_CONFIGURATION,
/**
* 在添加常规(非@Configuration)bean时,应该评估Condition。
* 该条件不会阻止添加@Configuration类。
* 在评估条件时,所有@Configuration类都将被解析。
*/
REGISTER_BEAN
}
}
ConditionEvaluator 用于评估条件
class ConditionEvaluator {
private final ConditionContextImpl context;
/**
* Create a new {@link ConditionEvaluator} instance.
*/
public ConditionEvaluator(@Nullable BeanDefinitionRegistry registry,
@Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {
this.context = new ConditionContextImpl(registry, environment, resourceLoader);
}
/**
* 根据@Conditional注释确定是否应跳过项目。ConfigurationCondition.ConfigurationPhase将从项目类型中推导出来
* (即@Configuration类将为ConfigurationCondition.ConfigurationPhase.PARSE_Configuration)
*/
public boolean shouldSkip(AnnotatedTypeMetadata metadata) {
return shouldSkip(metadata, null);
}
/**
*根据@Conditional注释确定是否应跳过项目
*/
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
//注释元数据是否为null
//元数据是否包含Conditional注解
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<>();
//所有condition类限定符名称
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
//反射创建condition
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;
}
@SuppressWarnings("unchecked")
private List<String[]> getConditionClasses(AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(Conditional.class.getName(), true);
Object values = (attributes != null ? attributes.get("value") : null);
return (List<String[]>) (values != null ? values : Collections.emptyList());
}
private Condition getCondition(String conditionClassName, @Nullable ClassLoader classloader) {
Class<?> conditionClass = ClassUtils.resolveClassName(conditionClassName, classloader);
return (Condition) BeanUtils.instantiateClass(conditionClass);
}
private static class ConditionContextImpl implements ConditionContext {
@Nullable
private final BeanDefinitionRegistry registry;
@Nullable
private final ConfigurableListableBeanFactory beanFactory;
private final Environment environment;
private final ResourceLoader resourceLoader;
@Nullable
private final ClassLoader classLoader;
public ConditionContextImpl(@Nullable BeanDefinitionRegistry registry,
@Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {
this.registry = registry;
this.beanFactory = deduceBeanFactory(registry);
this.environment = (environment != null ? environment : deduceEnvironment(registry));
this.resourceLoader = (resourceLoader != null ? resourceLoader : deduceResourceLoader(registry));
this.classLoader = deduceClassLoader(resourceLoader, this.beanFactory);
}
@Nullable
private ConfigurableListableBeanFactory deduceBeanFactory(@Nullable BeanDefinitionRegistry source) {
if (source instanceof ConfigurableListableBeanFactory) {
return (ConfigurableListableBeanFactory) source;
}
if (source instanceof ConfigurableApplicationContext) {
return (((ConfigurableApplicationContext) source).getBeanFactory());
}
return null;
}
private Environment deduceEnvironment(@Nullable BeanDefinitionRegistry source) {
if (source instanceof EnvironmentCapable) {
return ((EnvironmentCapable) source).getEnvironment();
}
return new StandardEnvironment();
}
private ResourceLoader deduceResourceLoader(@Nullable BeanDefinitionRegistry source) {
if (source instanceof ResourceLoader) {
return (ResourceLoader) source;
}
return new DefaultResourceLoader();
}
@Nullable
private ClassLoader deduceClassLoader(@Nullable ResourceLoader resourceLoader,
@Nullable ConfigurableListableBeanFactory beanFactory) {
if (resourceLoader != null) {
ClassLoader classLoader = resourceLoader.getClassLoader();
if (classLoader != null) {
return classLoader;
}
}
if (beanFactory != null) {
return beanFactory.getBeanClassLoader();
}
return ClassUtils.getDefaultClassLoader();
}
@Override
public BeanDefinitionRegistry getRegistry() {
Assert.state(this.registry != null, "No BeanDefinitionRegistry available");
return this.registry;
}
@Override
@Nullable
public ConfigurableListableBeanFactory getBeanFactory() {
return this.beanFactory;
}
@Override
public Environment getEnvironment() {
return this.environment;
}
@Override
public ResourceLoader getResourceLoader() {
return this.resourceLoader;
}
@Override
@Nullable
public ClassLoader getClassLoader() {
return this.classLoader;
}
}
}
比如在以下类中使用 ConditionEvaluator
- ConfigurationClassBeanDefinitionReader
- AnnotatedBeanDefinitionReader
- ClassPathScanningCandidateComponentProvider
- ConfigurationClassParser
ConfigurationClassUtils
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...
return hasBeanMethods(metadata);
}
static boolean hasBeanMethods(AnnotationMetadata metadata) {
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;
}
}
基于@Profile注释的值匹配的条件。
class ProfileCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) {
return true;
}
}
return false;
}
return true;
}
}
更多的条件注解在Spring Boot中大量定义使用;
每个条件注解的详细解析请查看对应文章比如Spring Boot 条件注解-@ConditionalOnBean | ConditionalOnMissingBean;