LOADING

加载过慢请开启缓存 浏览器默认开启

@Conditional注解

@Conditional的作用可以理解为满足一定条件就进行注入。

1. 简介

其源码如下:

@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接口中,只有一个matches方法:

@FunctionalInterface
public interface Condition {

    /**
     * Determine if the condition matches.
     * @param context the condition context
     * @param metadata the metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
     * or {@link org.springframework.core.type.MethodMetadata method} being checked
     * @return {@code true} if the condition matches and the component can be registered,
     * or {@code false} to veto the annotated component's registration
     */
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

当matches返回true时,将会进行注册。

2. SpringBootCondition

SpringBootCondition可以配合日志系统,帮助我们查看加载了哪些类。

我们只需要实现一个方法即可:

/**
 * Determine the outcome of the match along with suitable log output.
 * @param context the condition context
 * @param metadata the annotation metadata
 * @return the condition outcome
 */
public abstract ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata);

如果匹配,则直接返回ConditionOutcome.matches。或者在不匹配时调用ConditionOutcome.noMatch

3. 拓展

@ConditionalOnBean:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean。
@ConditionalOnClass:某个class位于类路径上,才会实例化一个Bean。
@ConditionalOnExpression:当表达式为true的时候,才会实例化一个Bean。
@ConditionalOnMissingBean:仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean。
@ConditionalOnMissingClass:某个class类路径上不存在的时候,才会实例化一个Bean。
@ConditionalOnNotWebApplication:不是web应用,才会实例化一个Bean。
@ConditionalOnBean:当容器中有指定Bean的条件下进行实例化。
@ConditionalOnMissingBean:当容器里没有指定Bean的条件下进行实例化。
@ConditionalOnClass:当classpath类路径下有指定类的条件下进行实例化。
@ConditionalOnMissingClass:当类路径下没有指定类的条件下进行实例化。
@ConditionalOnWebApplication:当项目是一个Web项目时进行实例化。
@ConditionalOnNotWebApplication:当项目不是一个Web项目时进行实例化。
@ConditionalOnProperty:当指定的属性有指定的值时进行实例化。
@ConditionalOnExpression:基于SpEL表达式的条件判断。
@ConditionalOnJava:当JVM版本为指定的版本范围时触发实例化。
@ConditionalOnResource:当类路径下有指定的资源时触发实例化。
@ConditionalOnJndi:在JNDI存在的条件下触发实例化。
@ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者有多个但是指定了首选的Bean时触发实例化。

4. 疑问

在看源码时,经常会发现一些Conditional的注解顶在一个静态类上,比如下面的:

private static class RetryMissingOrDisabledCondition extends AnyNestedCondition {

    RetryMissingOrDisabledCondition() {
        super(ConfigurationPhase.REGISTER_BEAN);
    }

    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
    static class RetryTemplateMissing {

    }

    @ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "false")
    static class RetryDisabled {

    }

}

网上查了一下没找到,所以自己打算测试一下这是干什么的。

首先我猜测的话:如果这个Condition为false,后面在外部的注册请求都会被拒绝。

首先我们写一个测试类:

// 这个condition肯定是false
@ConditionalOnProperty(value = "xxx", havingValue = "xxx")
public class TestClass {

    public TestClass() {
        System.out.println("被实例化了");
    }
}

然后在主类注册:

@SpringBootApplication
public class TestApplication {
    
    @Bean
    public TestClass testClass() {
        return new TestClass();
    }

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }
}

运行后发现这个类还是被实例化了。

猜测可能是需要在Condition类里使用:

public class MyCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return true;
    }

    @ConditionalOnProperty(value = "true", havingValue = "false")
    public static class Test {
        public Test() {
            System.out.println("被实例化了");
        }
    }

}

最后发现Test类还是会被注入。

emmm就先这样吧,如果以后发现有什么用了就再来补坑。