参考链接1
源码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
需要一个Condition的子类作为value参数
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
实现接口自定义判断逻辑,返回true代表条件成立,返回false代表条件不成立。
第一步:实现接口中的matches方法。
public class MyCondition1 implements org.springframework.context.annotation.Condition {
private final static Logger logger = LoggerFactory.getLogger(MyCondition1.class);
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
logger.info("reject...");
return false;
}
}
第二步:将 @Conditional(value = MyCondition1.class) 添加到配置类或者bean上。
class Condition1 implements org.springframework.context.annotation.Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
System.out.println(this.getClass().getName());
return true;
}
}
class Condition2 implements org.springframework.context.annotation.Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
System.out.println(this.getClass().getName());
return true;
}
}
class Condition3 implements org.springframework.context.annotation.Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
System.out.println(this.getClass().getName());
return true;
}
}
@Configuration
@Conditional({Condition1.class, Condition2.class, Condition3.class})
public class Config7 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config7.class);
}
}
输出
com.example.lurenjia.spring.c19.config.Condition1
com.example.lurenjia.spring.c19.config.Condition2
com.example.lurenjia.spring.c19.config.Condition3
默认情况下按照数组的顺序依次执行。
给第三个添加注解
@Order(1)
class Condition3 implements org.springframework.context.annotation.Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
System.out.println(this.getClass().getName());
return true;
}
}
输出
com.example.lurenjia.spring.c19.config.Condition3
com.example.lurenjia.spring.c19.config.Condition1
com.example.lurenjia.spring.c19.config.Condition2
class Condition3 implements org.springframework.context.annotation.Condition, Ordered {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
System.out.println(this.getClass().getName());
return true;
}
@Override
public int getOrder() {
return 0;
}
}
输出
com.example.lurenjia.spring.c19.config.Condition3
com.example.lurenjia.spring.c19.config.Condition1
com.example.lurenjia.spring.c19.config.Condition2
class Condition3 implements org.springframework.context.annotation.Condition, PriorityOrdered {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
System.out.println(this.getClass().getName());
return true;
}
@Override
public int getOrder() {
return 0;
}
}
输出
com.example.lurenjia.spring.c19.config.Condition3
com.example.lurenjia.spring.c19.config.Condition1
com.example.lurenjia.spring.c19.config.Condition2
配置类
@Configuration
@Import({Config1.class, Config2.class})
// @Import({Config1.class, Config3.class})
// @Import({Config1.class, Config4.class})
class ConfigAll {
}
@Configuration
class Config1 {
private static final Logger logger = LoggerFactory.getLogger(Config2.class);
@Bean
public String str1() {
logger.info("str1 created ");
return "str1";
}
}
@Conditional(value = My4Condition.class)
@Configuration
class Config2 {
private static final Logger logger = LoggerFactory.getLogger(Config2.class);
@Bean
public Integer int1() {
logger.info("int1 created ");
return Integer.valueOf(129);
}
}
@Configuration
class Config3 {
private static final Logger logger = LoggerFactory.getLogger(Config3.class);
@Conditional(value = My4Condition.class)
@Bean
public Integer int1() {
logger.info("int1 created ");
return Integer.valueOf(129);
}
}
@Conditional(value = My4ConfigurationCondition.class)
@Configuration
class Config4 {
private static final Logger logger = LoggerFactory.getLogger(Config4.class);
@Bean
public Integer int1() {
logger.info("int1 created ");
return Integer.valueOf(129);
}
}
启动类
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigAll.class);
}
}
My4Condition :判断容器中是否存在String类型bean
public class My4Condition implements org.springframework.context.annotation.Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Map<String, String> map = context.getBeanFactory().getBeansOfType(String.class);
System.out.println("map.size() = " + map.size());
return !map.isEmpty();
}
}
My4ConfigurationCondition :REGISTER_BEAN这个阶段判断
public class My4ConfigurationCondition implements ConfigurationCondition {
@Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.REGISTER_BEAN;
// return ConfigurationPhase.PARSE_CONFIGURATION;
}
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Map<String, String> map = context.getBeanFactory().getBeansOfType(String.class);
System.out.println("map.size() = " + map.size());
return !map.isEmpty();
}
}
正确的输出
str1 created
map.size() = 1
int1 created
错误的输出
map.size() = 0
str1 created
说明:Config2 中,注解解析的过程中无法获取到bean,Config3和Config4是正确的修改方式。
源码
public interface ConfigurationCondition extends Condition {
ConfigurationPhase getConfigurationPhase();
enum ConfigurationPhase {
PARSE_CONFIGURATION,
REGISTER_BEAN
}
}
ConfigurationCondition 可以指定PARSE_CONFIGURATION或者REGISTER_BEAN这两个阶段进行条件判断。
PARSE_CONFIGURATION:解析配置。
REGISTER_BEAN:注册bean。
比起Condition 更加细粒度的控制条件语句生效条件。
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {
Class<?>[] value() default {};
String[] type() default {};
Class<? extends Annotation>[] annotation() default {};
String[] name() default {};
SearchStrategy search() default SearchStrategy.ALL;
Class<?>[] parameterizedContainer() default {};
}
修饰范围:类型、方法。
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
Class<?>[] value() default {};
String[] name() default {};
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnCloudPlatformCondition.class)
public @interface ConditionalOnCloudPlatform {
/**
* The {@link CloudPlatform cloud platform} that must be active.
* @return the expected cloud platform
*/
CloudPlatform value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnExpressionCondition.class)
public @interface ConditionalOnExpression {
/**
* The SpEL expression to evaluate. Expression should return {@code true} if the
* condition passes or {@code false} if it fails.
* @return the SpEL expression
*/
String value() default "true";
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnJavaCondition.class)
public @interface ConditionalOnJava {
/**
* Configures whether the value configured in {@link #value()} shall be considered the
* upper exclusive or lower inclusive boundary. Defaults to
* {@link Range#EQUAL_OR_NEWER}.
* @return the range
*/
Range range() default Range.EQUAL_OR_NEWER;
/**
* The {@link JavaVersion} to check for. Use {@link #range()} to specify whether the
* configured value is an upper-exclusive or lower-inclusive boundary.
* @return the java version
*/
JavaVersion value();
/**
* Range options.
*/
enum Range {
/**
* Equal to, or newer than the specified {@link JavaVersion}.
*/
EQUAL_OR_NEWER,
/**
* Older than the specified {@link JavaVersion}.
*/
OLDER_THAN
}
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnJndiCondition.class)
public @interface ConditionalOnJndi {
/**
* JNDI Locations, one of which must exist. If no locations are specific the condition
* matches solely based on the presence of an {@link InitialContext}.
* @return the JNDI locations
*/
String[] value() default {};
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnMissingBean {
/**
* The class types of beans that should be checked. The condition matches when no bean
* of each class specified is contained in the {@link BeanFactory}.
* @return the class types of beans to check
*/
Class<?>[] value() default {};
/**
* The class type names of beans that should be checked. The condition matches when no
* bean of each class specified is contained in the {@link BeanFactory}.
* @return the class type names of beans to check
*/
String[] type() default {};
/**
* The class types of beans that should be ignored when identifying matching beans.
* @return the class types of beans to ignore
* @since 1.2.5
*/
Class<?>[] ignored() default {};
/**
* The class type names of beans that should be ignored when identifying matching
* beans.
* @return the class type names of beans to ignore
* @since 1.2.5
*/
String[] ignoredType() default {};
/**
* The annotation type decorating a bean that should be checked. The condition matches
* when each annotation specified is missing from all beans in the
* {@link BeanFactory}.
* @return the class-level annotation types to check
*/
Class<? extends Annotation>[] annotation() default {};
/**
* The names of beans to check. The condition matches when each bean name specified is
* missing in the {@link BeanFactory}.
* @return the names of beans to check
*/
String[] name() default {};
/**
* Strategy to decide if the application context hierarchy (parent contexts) should be
* considered.
* @return the search strategy
*/
SearchStrategy search() default SearchStrategy.ALL;
/**
* Additional classes that may contain the specified bean types within their generic
* parameters. For example, an annotation declaring {@code value=Name.class} and
* {@code parameterizedContainer=NameRegistration.class} would detect both
* {@code Name} and {@code NameRegistration}.
* @return the container types
* @since 2.1.0
*/
Class<?>[] parameterizedContainer() default {};
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnMissingClass {
/**
* The names of the classes that must not be present.
* @return the names of the classes that must not be present
*/
String[] value() default {};
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnWebApplicationCondition.class)
public @interface ConditionalOnNotWebApplication {
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
String[] value() default {};
String prefix() default "";
String[] name() default {};
String havingValue() default "";
boolean matchIfMissing() default false;
}
value和name互为别名,prefix为前缀。
@Configuration(proxyBeanMethods = false)
public class ConditionalOnPropertyConfig {
private static final Logger logger = LoggerFactory.getLogger(ConditionalOnPropertyConfig.class);
@ConditionalOnProperty(prefix = "str", value = "enable1",
havingValue = "true", matchIfMissing = true)
@Bean
public String str1() {
logger.info("str1 create success");
return "str1";
}
@ConditionalOnProperty(prefix = "str", value = "enable2",
havingValue = "true", matchIfMissing = false)
@Bean
public String str2() {
logger.info("str2 create success");
return "str2";
}
@ConditionalOnProperty(prefix = "str", value = "enable3",
matchIfMissing = true)
@Bean
public String str3() {
logger.info("str3 create success");
return "str3";
}
@ConditionalOnProperty(prefix = "str", value = "enable4",
matchIfMissing = false)
@Bean
public String str4() {
logger.info("str4 create success");
return "str4";
}
}
yml不做配置,输出如下
ConditionalOnPropertyConfig : str1 create success
ConditionalOnPropertyConfig : str3 create success
也就是 matchIfMissing = true 的时候,没有配置文件一样会注册bean。
yml新增如下代码
str:
enable1: truee
enable2: truee
enable3: truee
enable4: truee
输出
str3 create success
str4 create success
当prefix+value能够在yml中找到的啥时候,会注册havingValue没有指定值的情况。
将truee改为true,四个bean都可以注册成功。改为false则一个都不会注册。
在注解 If not specified, the property must not be equal to false.有这么一段话,如果没有指定,这个属性不能等于false
ConditionalOnProperty 代表从配置文件中读取条件,value跟name互为别名选一个即可,prefix是前缀。
matchIfMissing如果为true代表配置文件中即使未匹配到key,该注解返回true。
havingValue不指定,当value不为false的其他情况条件注解返回true,指定则必须严格匹配value值才会返回true。
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnResourceCondition.class)
public @interface ConditionalOnResource {
/**
* The resources that must be present.
* @return the resource paths that must be present.
*/
String[] resources() default {};
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnSingleCandidate {
/**
* The class type of bean that should be checked. The condition matches if a bean of
* the class specified is contained in the {@link BeanFactory} and a primary candidate
* exists in case of multiple instances.
*
* This attribute may not be used in conjunction with
* {@link #type()}, but it may be used instead of {@link #type()}.
* @return the class type of the bean to check
*/
Class<?> value() default Object.class;
/**
* The class type name of bean that should be checked. The condition matches if a bean
* of the class specified is contained in the {@link BeanFactory} and a primary
* candidate exists in case of multiple instances.
*
* This attribute may not be used in conjunction with
* {@link #value()}, but it may be used instead of {@link #value()}.
* @return the class type name of the bean to check
*/
String type() default "";
/**
* Strategy to decide if the application context hierarchy (parent contexts) should be
* considered.
* @return the search strategy
*/
SearchStrategy search() default SearchStrategy.ALL;
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnWarDeploymentCondition.class)
public @interface ConditionalOnWarDeployment {
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnWebApplicationCondition.class)
public @interface ConditionalOnWebApplication {
/**
* The required type of the web application.
* @return the required web application type
*/
Type type() default Type.ANY;
/**
* Available application types.
*/
enum Type {
/**
* Any web application will match.
*/
ANY,
/**
* Only servlet-based web application will match.
*/
SERVLET,
/**
* Only reactive-based web application will match.
*/
REACTIVE
}
}