注解和Predicate组合拓展

一. 背景:

最近小熙被问到一个场景拓展,是关于匹配规则的设计如何高拓展,由此将实现分享给大家。

二. 代码:

1. 介绍:

在经历技术调研和场景业务分析以及后续的考虑之后,最终没有选择规则引擎这些,而是采用更为切合业务的自研(主要也是由于规则简单,但又多变),分两部分 注解释意Predicate过滤

2. 注解代码:

其中注解是本业务中使用的(方便举例),可以直接看注解工具类

(1)注解

目前业务只需获取name属性,如有其他需求可以自行拓展

/**
 * @author chengxi
 * @date 2023/3/27 17:21
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD})
public @interface JudgeExpressExcludeField {

    public String name() default "";

}
/**
 * @author chengxi
 * @date 2023/3/27 17:08
 */
@Documented
@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface JudgeExpressMatchField {

    public String name() default "";
}

(2)注解工具类:

这里目前主要是获取name的value,如有拓展需要自行编写

/**
 * @author chengxi
 * @date 2023/3/28 11:10
 * @blog https://blog.csdn.net/weixin_41133233?type=blog
 */
public class AnnotationUtil {

    /**
     * 获取注解key-value-list
     *
     * @param object
     * @param annotationClass
     * @return
     * @param <T>
     */
    public static <T extends Annotation> List<KeyValueDto> queryAnnotationKeyValueList(Object object, Class<? extends Annotation> annotationClass) {
        //KeyValueDto是实体类方便返回也可以用map
        List<KeyValueDto> keyValueDtoList = new ArrayList();
        try {
            //java反射机制获取所有字段名称
            Field[] fields = object.getClass().getDeclaredFields();
            //遍历循环方法并获取对应的字段名称
            for (Field field : fields) {
                field.setAccessible(true);
                // 判断是否方法上存在注解  JudgeExpressMatchField
                boolean annotationPresent = field.isAnnotationPresent(annotationClass);
                if (annotationPresent) {
                    // 获取自定义注解对象
                    String keyName = "";
                    T annotation = (T) field.getAnnotation(annotationClass);
                    //获取被代理的对象
                    InvocationHandler invo = Proxy.getInvocationHandler(annotation);
                    Map map = (Map) getFieldValue(invo, "memberValues");
                    if (map != null) {
                        keyName = (String) map.get("name");
                    }
                    if (StringUtils.isNotEmpty(keyName)) {
                        // 根据对象获取注解值
                        Object o = field.get(object);
                        keyValueDtoList.add(new KeyValueDto(keyName, o == null ?"":o.toString()));
                    }
                }
            }
            // 排序(按照方法名称排序)
            Collections.sort(keyValueDtoList);
        } catch (Exception e) {
            PrintUtil.printCatchException(e, "获取注解key-value-list异常:");
        }
        return keyValueDtoList;
    }

    /**
     * 获取字段值
     *
     * @param object
     * @param property
     * @return
     * @param <T>
     */
    public static <T> Object getFieldValue(T object, String property) {
        if (object != null && property != null) {
            Class<T> currClass = (Class<T>) object.getClass();

            try {
                Field field = currClass.getDeclaredField(property);
                field.setAccessible(true);
                return field.get(object);
            } catch (NoSuchFieldException e) {
                PrintUtil.printCatchException(e, currClass + " has no property: " + property);
                throw new IllegalArgumentException(currClass + " has no property: " + property);
            } catch (IllegalArgumentException e) {
                throw e;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

}

3. KeyValueDto实体类代码:

/**
 * @author chengxi
 * @date 2023/3/27 17:21
 */
public class KeyValueDto implements Comparable<KeyValueDto>{
    private String key;
    private String value;
 
    public KeyValueDto(String key, String value) {
        this.key = key;
        this.value = value;
    }
 
    public String getKey() {
        return key;
    }
 
    public void setKey(String key) {
        this.key = key;
    }
 
    public String getValue() {
        return value;
    }
 
    public void setValue(String value) {
        this.value = value;
    }
 
    @Override
    public int compareTo(KeyValueDto o) {
        return o.getKey().compareTo(this.key);
    }
 
    @Override
    public String toString() {
        return "{" +
                "key='" + key + '\'' +
                ", value='" + value + '\'' +
                '}';
    }
}

4. 实体里注解使用代码:

此处截取一部分展示,注意匹配和校验的name是需要一一对应的

(1)JudgeExpressMatchField注解:

    /**
     * 省
     */
    @JudgeExpressMatchField(name = "province")
    private String province;
    /**
     * 市
     */
    @JudgeExpressMatchField(name = "city")
    private String city;
    /**
     * 区
     */
    @JudgeExpressMatchField(name = "district")
    private String district;

(2)JudgeExpressExcludeField注解:

    /**
     * 省
     */
    @JudgeExpressExcludeField(name = "province")
    private String province;
    /**
     * 市
     */
    @JudgeExpressExcludeField(name = "city")
    private String city;
    /**
     * 区
     */
    @JudgeExpressExcludeField(name = "district")
    private String district;

5. 示例业务实现代码:

(1)在filter中调用此Predicate
(2)此处示例,是将符合排除规则的,仅能匹配规则的,做对应的过滤
(3)在不变大体业务逻辑下,这里是不需要修改的,只需在对应实体类中,将比较的字段加上对应注解,即可自动拓展

    @Override
    public Predicate<RulesDto> judgeExpressMatch(DocOrderHeaderEntity docOrderHeaderEntity) {
        return rulesDto -> {
            Map<String, String> entryParamMap = MapUtils.listObjectToMap(AnnotationUtil.queryAnnotationKeyValueList(docOrderHeaderEntity, JudgeExpressMatchField.class));
            List<KeyValueDto> ruleList = AnnotationUtil.queryAnnotationKeyValueList(RulesDto.getClass(), rulesDto, JudgeExpressMatchField.class);
            for (ExcludeRuleDto excludeRuleDto : rulesDto.getExcludeRuleDtoList()) {
                List<KeyValueDto> excludeRuleList = AnnotationUtil.queryAnnotationKeyValueList(excludeRuleDto, JudgeExpressExcludeField.class);
                for (KeyValueDto keyValueDto : excludeRuleList) {
                    String value = entryParamMap.get(keyValueDto.getKey());
                    if (StringUtils.isNotBlank(keyValueDto.getValue()) && keyValueDto.getValue().equalsIgnoreCase(value)) {
                        return Boolean.FALSE;
                    }
                }
            }
            // todo 此处和上述过虑类似,对ruleList规则并且操作,返回对应boolean
            return Boolean.TRUE;
        };
    }

6. 业务调用示例代码:

			// 循环获取到的生效规则集合,筛选出最优
            Optional<DeliveryRulesDto> firstDeliveryRule = deliveryRulesDtoList.stream()
                    // 调用自定义实现的Predicate,并输入业务entity进行过滤适配
                    .filter(carrierReplaceCommonService.judgeExpressMatch(docOrderHeaderEntity))
                    // 通过事先预定好的顺序,排序
                    .sorted((s1, s2) -> s1.getPriority().compareTo(s2.getPriority()))
                    .findFirst();
            if (firstDeliveryRule.isPresent()) {
                // 获取优先级最高的规则
                String ruleDetail = firstDeliveryRule.get().getRuleDetail();
                // todo 收集,根据规则修改后的订单,并执行接下来的业务
            }

三. 后语:

(1)上述就是小熙对于此处业务场景的,简单示例编写了。
(2)当然此处示例是当前的版本一,还有一些拓展和优化待后续更新。
(3)当然也有其他更好的办法,根据自己的业务和喜好抉择就好。

如果本次分享帮助到了你,还请点个赞哦~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值