Spring Boot源码中大量使用@ConditionalOnProperty
来控制Configuration
是否生效。
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
/**
* 数组,获取对应property名称的值,与name不可同时使用。
*
* Alias for {@link #name()}.
* @return the names
*/
String[] value() default {};
/**
* property名称的前缀,可有可无,根据事情情况进行配置。
* 前缀将会应用于每个属性的前缀。如果未指定,前缀将自动结束.
*
* A prefix that should be applied to each property. The prefix automatically ends
* with a dot if not specified.
* @return the prefix
*/
String prefix() default "";
/**
* 数组,property完整名称或部分名称(可与prefix组合使用,组成完整的property名称),与value不可同时使用。
*
* The name of the properties to test. If a prefix has been defined, it is applied to
* compute the full key of each property. For instance if the prefix is
* {@code app.config} and one value is {@code my-value}, the fully key would be
* {@code app.config.my-value}
*
* Use the dashed notation to specify each property, that is all lower case with a "-"
* to separate words (e.g. {@code my-long-property}).
* @return the names
*/
String[] name() default {};
/**
* 可与name组合使用,比较获取到的属性值与havingValue给定的值是否相同,相同才加载配置。
*
* The string representation of the expected value for the properties. If not
* specified, the property must not be equals to {@code false}.
* @return the expected value
*/
String havingValue() default "";
/**
* 缺少该property时是否可以加载。如果为true,没有该property也会正常加载;反之报错
*
* Specify if the condition should match if the property is not set. Defaults to
* {@code false}.
* @return if should match if the property is missing
*/
boolean matchIfMissing() default false;
}
单纯看上面的说明还是很抽象,下面我们举一些例子来帮助我们理解。
基础类
public class User {
}
public class Dept {
}
配置类
// 意思:当配置文件中只要出现config1.enable时(内容为空,同样也是有效的),则该配置生效。
@Configuration
@ConditionalOnProperty(name = "config1.enable")
public class Config1 {
@Bean
public User user() {
System.out.println("------------------初始化User----------------");
return new User();
}
}
// 意思:当配置文件中只要出现config2.enable时(内容为空,同样也是有效的),则该配置生效。
@Configuration
@ConditionalOnProperty(name = "config2.enable")
public class Config2 {
@Bean
public Dept dept() {
System.out.println("------------------初始化Dept----------------");
return new Dept();
}
}
启动类
@SpringBootApplication
public class ConditionApplication {
public static void main(String[] args) {
SpringApplication.run(ConditionApplication.class, args);
}
}
配置文件
config1:
enable: test
启动效果
根据上述配置文件的配置,我们看到Config1
配置生效了,并打印了创建了User
这个Bean的日志。而Config2
则没有打印,也就意味着其没有生效。
在上述代码的基础上,我们首先的Config1
和Config2
中稍作一些修改。
代码
// 当配置文件中config1.enable的值为true才加载,否则就不加载
@Configuration
@ConditionalOnProperty(name = "config1.enable", havingValue = "true")
public class Config1 {
@Bean
public User user() {
System.out.println("------------------初始化User----------------");
return new User();
}
}
// 当配置文件中config1.enable的值为true才加载,否则就不加载
@Configuration
@ConditionalOnProperty(name = "config2.enable", havingValue = "true")
public class Config2 {
@Bean
public Dept dept() {
System.out.println("------------------初始化Dept----------------");
return new Dept();
}
}
配置文件
config1:
enable: false
config2:
enable: true
效果
我们发现User没有初始化,而Dept则进行了初始化。
随着功能的迭代,我们又更多类似的初始化功能,例如创建User、Dept、Menu、Role。依样画葫芦,application.yml中多了相似的配置。
config1:
enable: false
config2:
enable: true
config3:
enable: true
# 等等...
这样的配置有很大的优化空间,我们可能需要一个的创建的功能,同时也要保留原来批量创建的功能。
形如下面
config:
all: true
这个有点麻烦,涉及到多个条件的组合。ConditionalOnProperty是支持 “多个条件逻辑与”的。
@ConditionalOnProperty(name={"config1.enable","config.all"}, havingValue="true")
这样的条件是必须都是true才会加载。限制很大。
而我们实际项目往往需要的,只要有一个是true
则就进行加载。所以此时ConditionalOnProperty
并不能解决我们的问题。
注意: 1:ConditionalOnProperty 本身也并没有这样的功能
2: 一个类的头部不能添加多个ConditionalOnProperty,所以这种方式是不可能的
3:一种繁琐的做法,是自定义条件,继承AnyNestedCondition(这里不做探讨,感兴趣的同学可以去了解下)
4:使用@ConditionalOnExpression注解
代码如下
@ConditionalOnExpression(value = "${config2.enable:false} || ${config.all:false}")
这样既实现了其中两个值,只要有一个值是true,就会进行加载Config配置类。
但是这样做还是有问题。我们可以其看成局部配置和全部配置的关系处理。想要的达到的效果如下:
如果局部有配置,则按局部配置处理。如果局部没配置,则按全局配置处理。如果全局、局部都没配置,则默认不导入。这里可以利用“默认值”嵌套表达式实现。
其实这样的效果,往往是我们实际项目需要的比较多的使用场景。
所以,嵌套表达式闪亮登场。
@ConditionalOnExpression(value = "${config2.enable:${config.all:false}}")
感兴趣的同学可以,修改配置文件的代码,启动起来看看效果,我在这里就不演示了。