00:00:00
自定义多值注入条件判断注解
一、同一个 key 多个值 or 匹配的 Conditional 注解
这种需求可以在 spring boot 中有多种实现方式
- 可以使用
@Conditional注解加自定义继承了AnyNestedCondition的 Conditional 类进行 - 自定义自己的注解
.......
此文章重点关注自定义多值匹配注解,及其实现了 Conditional 接口的类
1.1 注解定义 @ConditionalOnMultiValueProperty
自定义注解取名为 ConditionalOnMultiValueProperty 用于取当前系统配置文件中的某个指定的配置 key ,定义注解与标准的 @Conditional 衍生注解类似 都是复合注解,在注解上都需要打上指定的 @Conditional 注解并指定实现的 Conditional 类,定义如下
java
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
// 声明当前注解使用哪个 Conditional 类
@Conditional(MultiValuePropertyCondition.class)
public @interface ConditionalOnMultiValueProperty {
/**
* 配置文件的 key
*
* @return String
*/
String property();
/**
* 匹配的值
*
* @return String[]
*/
String[] values();
/**
* 匹配模式
*
* @return MatchModel
*/
MatchModel matchMode() default MatchModel.EXACT;
/**
* 如果缺失配置的情况下是否默认匹配成功
*
* @return boolean
*/
boolean missingMatch() default false;
}1.2 Conditional 接口实现类
此接口实现类用于匹配配置值和当前指定值是否匹配,定义此类有两种实现,在容器运行中或者使用注解实现,定义如下
java
public class MultiValuePropertyCondition implements Condition {
/**
* 读取的配置前缀
*/
private String propertyName;
/**
* 待匹配的值
*/
private String[] expectedValues;
/**
* 匹配模式
*/
private MatchModel matchMode;
/**
* 缺少配置时是否默认匹配成功
*/
private boolean missing;
public MultiValuePropertyCondition(String propertyName, MatchModel matchMode, boolean missing, String... expectedValues) {
this.propertyName = propertyName;
this.expectedValues = expectedValues;
this.matchMode = matchMode;
this.missing = missing;
}
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 如果是通过注解使用的,从注解获取参数
if (metadata.isAnnotated(ConditionalOnMultiValueProperty.class.getName())) {
return matchesAnnotation(context, metadata);
}
// 如果是通过构造器创建的,使用构造器参数
return matchesConstructor(context);
}
/**
* 来自构造函数的匹配
*
* @param context 匹配上下文
* @return boolean
*/
private boolean matchesConstructor(ConditionContext context) {
return doMatches(context, this.propertyName, this.matchMode, this.missing, this.expectedValues);
}
/**
* 来自注解的匹配
*
* @param context 匹配上下文
* @param metadata 注解配置信息
* @return boolean
*/
private boolean matchesAnnotation(ConditionContext context, AnnotatedTypeMetadata metadata) {
MergedAnnotation<ConditionalOnMultiValueProperty> mergedAnnotation = metadata.getAnnotations().get(ConditionalOnMultiValueProperty.class);
String[] expectedValues = mergedAnnotation.getStringArray("values");
MatchModel matchMode = mergedAnnotation.getEnum("matchMode", MatchModel.class);
boolean missingMatch = mergedAnnotation.getBoolean("missingMatch");
String property = mergedAnnotation.getString("property");
return doMatches(context, property, matchMode, missingMatch, expectedValues);
}
/**
* 实现匹配逻辑
*
* @param context 匹配上下文
* @param propertyName 读取的配置前缀
* @param matchMode 匹配模式
* @param missing 缺少配置时是否默认匹配成功
* @param expectedValues 待匹配的值
* @return boolean
*/
private boolean doMatches(ConditionContext context, String propertyName, MatchModel matchMode, boolean missing, String[] expectedValues) {
// 如果当前注入判断的获取配置类的 key 为空或者待匹配的数组为空,则直接返回 false
if (StrUtil.isBlank(propertyName) || ArrayUtil.isEmpty(expectedValues)) {
return false;
}
// 实现匹配逻辑
Environment env = context.getEnvironment();
// 获取配置信息
String propertyValue = env.getProperty(propertyName);
// 判断当前配置信息中是否有需要判断的配置 key 如果没有获取到,则返回配置的 missing 值
if (StrUtil.isBlank(propertyValue)) {
return missing;
}
// 获取当前配置的值,通过正则表达式切割成数组
String[] actualValues = propertyValue.split("[,;]");
// 将数组转换成集合,过滤为空的字符串并且去除前后空格
List<String> actualValueList = Arrays.stream(actualValues)
.filter(StrUtil::isNotBlank)
.map(item -> StrUtil.trim(item.toUpperCase()))
.toList();
// 将数组转换成集合,过滤为空的字符串并且去除前后空格
List<String> expectedValueList = Arrays.stream(expectedValues)
.filter(StrUtil::isNotBlank)
.map(item -> StrUtil.trim(item.toUpperCase()))
.toList();
return switch (matchMode) {
case EXACT -> Arrays.equals(actualValues, expectedValues);
case ANY -> !Collections.disjoint(actualValueList, expectedValueList);
};
}
}1.3 使用示例
java
@Bean
@ConditionalOnMultiValueProperty(property ="business.a.b.config-type" , values = {"DB_CONFIG","MQ_CONFIG"}, matchMode = MatchModel.ANY,missingMatch = true)
public BusinessDbMqConfig createEnvConfigLoading() {
return new BusinessDbMqConfig();
}