Skip to content
00:00:00
0

自定义多值注入条件判断注解

一、同一个 key 多个值 or 匹配的 Conditional 注解

这种需求可以在 spring boot 中有多种实现方式

  1. 可以使用 @Conditional 注解加自定义继承了 AnyNestedCondition 的 Conditional 类进行
  2. 自定义自己的注解
    .......

此文章重点关注自定义多值匹配注解,及其实现了 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();
}
最近更新