工具类-封装通用开关

前言

在实际项目中,相信很多都会用到数字字典之类的配置功能,其作用之一就是配置开关功能。针对开关问题,写了一个简单的通用版本用于抛砖引玉。

一、设计思路

自定义注解+AOP+SPEL解析,使用策略模式进行不同验证需求的验证

二、具体实现

1.自定义注解

自定义注解需要设置的值
values:表示需要进行验证的字段和在数字字典存的值的key,这里是数组,表明可以接收多个字段和key
messages:表示验证出错时,需要返回的报错信息,可以于values长度对应,也可以共用一个。
rules:验证规则,表示验证的规则,可以和前面的字段对应,也可以共用一个
注意:当messages和rules少于values时,取的是最后一个message和rule。

/**
 * 通过开关注解
 * @author jy
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface UniversalSwitch {

    /**
     *
     * @return
     */
    String[] values ();

    /**
     * 报错信息
     * @return
     */
    String[] messages ();
    /**
     * 规则
     * @return
     */
    SwitchRuleEnum[] rules();
}

2.规则枚举

枚举中code代表的是后期具体规则验证类的beanName,如果有需要扩展规则的在此添加。

/**
 * 比较规则
 * @author jy
 */

public enum SwitchRuleEnum {
    //大于
    GREATER("greaterSwitchRule","大于"),
    //大于等于
    GREATER_OR_EQUAL("greaterOrEqualSwitchRule","大于等于"),
    //小于
    LESS("lessSwitchRule","小于"),
    //小于等于
    LESS_OR_EQUAL("lessOrEqualSwitchRule","小于等于"),
    //等于
    EQUAL("equalSwitchRule","等于"),
    //不等于
    UNEQUAL("unequalSwitchRule","不等于"),
    //不为空
    NOT_EMPTY("notEmptySwitchRule","不为空"),
    ;
    private String code;
    private String description;

    SwitchRuleEnum(String code, String description) {
        this.code = code;
        this.description = description;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
    public static SwitchRuleEnum getByType(String code) {
        for (SwitchRuleEnum s : values()) {
            if (s.getCode().equals(code)) {
                return s;
            }
        }
        return null;
    }

    public static String getName(String code) {
        for (SwitchRuleEnum c : SwitchRuleEnum.values()) {
            if (c.code.equals(code)) {
                return c.name();
            }
        }
        return null;
    }
}

3.AOP实现

通过SPEL解析到字段中的具体值,该值就是待验证的值,和数字字段中的进行比较。
利用ApplicationContext获取到具体的验证规则类。

/**
 * @author jy
 */
@Aspect
@Component
public class UniversalSwitchAop implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    /**
     * 需要被解析的前后缀
     */
    public static final TemplateParserContext TEMPLATE_PARSER_CONTEXT = new TemplateParserContext("{", "}");

    /**
     * Spell解析器
     */
    public static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();

    @Pointcut("@annotation(com.dili.ann.UniversalSwitch)")
    public void dsPointCut() {
    }

    @After("dsPointCut()")
    public Object around(JoinPoint point) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        //获取参数名称
        String[] parameterNames = signature.getParameterNames();
        //获取参数
        Object[] args = point.getArgs();
        //把handleMethod的形参都添加到上下文中,并使用参数名作为key
        EvaluationContext context = new StandardEvaluationContext();
        int length = args.length;
        for (int i = 0; i < length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }
        UniversalSwitch annotation = method.getAnnotation(UniversalSwitch.class);
        if (annotation != null) {
            String[] values = annotation.values();
            String[] messages = annotation.messages();
            SwitchRuleEnum[] r = annotation.rules();
            int valLength = values.length;
            int msgLength = messages.length;
            int sLength = r.length;
            for (int i = 0; i < valLength; i++) {
                String o = EXPRESSION_PARSER.parseExpression(values[i], TEMPLATE_PARSER_CONTEXT).getValue(context, String.class);
                //报错信息可以允许公用,但是如果是少于检验字段的话,少的部分使用最后一个报错信息
                String msg = i+1 > msgLength ? messages[msgLength - 1] : messages[i];
                //检验规则允许公用,同理少于部分使用的是最后一个规则
                SwitchRuleEnum switchRuleEnum = i+1 > sLength ? r[sLength - 1] : r[i];
                applicationContext.getBean(switchRuleEnum.getCode(), SwitchRule.class).execute(o, msg);
            }
        }
        return point.getTarget();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

4.验证接口

/**
 * 验证
 * @author jy
 */
public interface SwitchRule {
    /**
     * 执行校验
     * @param val
     * @param msg
     */
    void execute(String val, String msg);
}

5.验证实现

这里的实现只是测试代码,具体实现最好从数字字典中获取。
注意:val按=拆分为数组后下标0的值对应的应该是数字字典或redis中的key

/**
 * 验证大于
 * @author jy
 */
@Service(value = "greaterSwitchRule")
public class SwitchRule4Greater implements SwitchRule{
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public void execute(String val, String msg) {
        //val中包含了数字字典或者redis中的key和需要被校验的值,按=分隔
        String[] split = val.split("=");
        String s = stringRedisTemplate.opsForValue().get(split[0]);
        //如果没有值,则不需判断
        if (StrUtil.isBlank(s)){
            return;
        }
        String str = split.length>1?split[1]:"0";
        //下面则是判断这个入参不能大于查回的值 100写死的,实际中应该是从数字字典或redis中获取到的
        TUtils.isTure(str.compareTo("100")>0).throwMessage(msg);
    }
}

TUtils这个类的具体使用可以参考[工具类-if判断]

https://blog.csdn.net/qq_41792853/article/details/131010598?spm=1001.2014.3001.5502

5.测试验证

/**
 * 通用开关
 * @author jy
 */
@RestController
@RequestMapping("/switch")
@Slf4j
public class UniversalSwitchController {
    @UniversalSwitch(values = {"code={#accountCycleDo.userCode}","balance={#accountCycleDo.balance}"}, 
            messages = {"操作员编号不能为空","盘存金额不能大于100"},rules = {SwitchRuleEnum.NOT_EMPTY,SwitchRuleEnum.GREATER})
    @RequestMapping("/test")
    public BaseOutput<String> test(@RequestBody AccountCycleDo accountCycleDo){
        return BaseOutput.success();
    }
}

这样就可以进行值验证了。

总结

写得比较粗略,实现也只写了一个,具体的肯定要都实现。就当一个思路吧,重要的是总结自己实际业务中的需求,积累自己的工具库,不断提升吧,大家加油!!!!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值