本章节将介绍我们自定义 @Conditional 注解的条件。
在 @Conditional 注解的源码中,value 属性接收一个类型为 Condition 的 Class 数组。源码如下:
@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 接口定义了 @Conditional 注解的条件,你可以为 @Conditional 注解指定一个或多个条件,只有当匹配了所有指定的条件才能成功注册组件。
Condition 将在即将注册 Bean 定义之前进行检查,并且可以根据它的 matches() 方法返回值动态决定是否注册组件(true-允许注册;false-不允许注册)。
注意:Condition 必须遵循与 BeanFactoryPostProcessor 相同的限制,并注意永远不要与 bean 实例交互。对于与 @Configuration bean 交互的条件的更细粒度控制,请考虑实现 ConfigurationCondition 接口。
Condition 接口源码如下:
@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,否则返回false)。
我们通过一个示例来演示自己实现 Condition 接口,然后配合 @Conditional 注解一起使用。然后在 @Configuration 配置上动态决定是否将该配置下面的 bean 注册到 Spring 容器中。详细代码如下:
(1)实现 Condition 接口,自定义条件 MyCondition。代码如下:
public class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 这里什么业务逻辑也没有,直接返回结果,仅仅为了方便测试
return true;
}
}(2)编写两个自己的服务,代码如下:
a、Service1.java
public class Service1 {
public String show() {
return Service1.class.getName();
}
}b、Service2.java
public class Service2 {
public String show() {
return Service2.class.getName();
}
}(3)创建一个 @Configuration 配置类,代码如下:
@Configuration
@Conditional(MyCondition.class)
public class MyConfig {
@Bean
public Service1 service1() {
return new Service1();
}
@Bean
public Service2 service2() {
return new Service2();
}
}上面配置类将根据 @Conditional(MyCondition.class) 注解中指定的 MyCondition 类的 matches() 方法返回结果动态决定是否将 Service1 和 Service2 实例化且注册到 Spring 容器中。
(4)客户端。用来验证我们是否真的根据 MyCondition 动态将 Service1 和 Service2 实例注册到 Spring 容器中。代码如下:
import com.huangx.springboot.autoconfig.service.Service1;
import com.huangx.springboot.autoconfig.service.Service2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@SpringBootApplication
public class SpringbootAutoconfigDemo1Application {
@Autowired
private ApplicationContext context;
public static void main(String[] args) {
SpringApplication.run(SpringbootAutoconfigDemo1Application.class, args);
}
@GetMapping("/")
public String index() {
Service1 service1 = null;
try {
service1 = context.getBean(Service1.class);
} catch (Exception e) {
System.err.println(e.getMessage());
}
Service2 service2 = null;
try {
service2 = context.getBean(Service2.class);
} catch (Exception e) {
System.err.println(e.getMessage());
}
return "successful<br/>" +
"service1=" + service1 + "</br>" +
"service2=" + service2;
}
}如果 MyCondition.matches() 方法返回 true,你访问 http://localhost:8080 结果如下图:

如果 MyCondition.matches() 方法返回 false,你访问 http://localhost:8080 结果如下图:

上面仅仅演示了最最简单的用法,你还可以根据 Java 中的属性值、JDK版本、操作系统类型等等不同的条件,动态选择配置和初始化。