基于SpringBoot 、AOP与自定义注解转义字典值

  一直以来,前端展示字典一般以中文展示为主,若在表中存字典值中文,当字典表更改字典值对应的中文,会造成数据不一致,为此设置冗余字段并非最优方案,若由前端自己写死转义,不够灵活,若在业务代码转义,臃肿也不够通用,从网络上了解到注解、AOP是一种不错的解决方案,主要有两种方式:

  1、通过注解获取结果集转为JSON字符串,通过正则查找附加字段;

  2、通过获取结果集中相关字段上注解,此种方法有两个需要解决的问题,父类继承字段、嵌合对象难以解决获取对应注解字段问题,解决起来均比较麻烦;

  因此本文采用第一种方法,能有效规避第二种方法相关问题,做到逻辑相对简单,引入缓存提高效率。

一、新建注解

  标注方法上使用

1 @Retention(RetentionPolicy.RUNTIME)
2 @Target(ElementType.METHOD)
3 @Documented
4 public @interface TranslationDict {
5     FieldParam[] value();
6 }

  注解参数:FieldParam

 1 @Retention(RetentionPolicy.RUNTIME)
 2 @Target({ElementType.FIELD})
 3 @Documented
 4 public @interface FieldParam {
 5 
 6     /**
 7      * 字段类型 默认字典 
 8      * Constant.FIELDTYPE_DICT 为自定义常量
 9      * @return
10      */
11     int type() default Constant.FIELDTYPE_DICT;
12 
13     /**
14      * 字典dictType
15      * @return
16      */
17     String dictType() default "";
18 
19     /**
20      * 要翻译的字段 目标字段为翻译的字段+Str
21      * @return
22      */
23     String targetField() default "";
24 
25     /**
26      * 要翻译的字段值类型
27      * @return
28      */
29     Class targetFieldValueClazz() default String.class;
30 
31 }

二、注解的使用

  在需要转义方法体上添加注解,在注解上指定需要转义的字段,不声明则使用默认值。

@TranslationDict({@FieldParam(dictType = "CUSTOMER_SEX", targetField = "sex"),
            @FieldParam(dictType = "CUSTOMER_STATUS", targetField = "status", targetFieldValueClazz = Integer.class)})

三、新建切面

  切面核心在于将结果集转为JSON字符串,通过正则查询需要转义的字段,进行拼接替换,以增加属性。

  1 @Aspect
  2 @Component
  3 @Slf4j
  4 public class TranslateFieldAspect {
  5 
  6     /**
  7      * 翻译字典值
  8      * @param joinPoint
  9      * @return
 10      * @throws Throwable
 11      */
 12     @Around("@annotation(com.vfangtuan.vft.common.annotation.TranslationDict)")
 13     public Object aroundMethodDict(ProceedingJoinPoint joinPoint) throws Throwable {
 14         //接收到请求时间
 15         Long startTime = System.currentTimeMillis();
 16         //注意,如果调用joinPoint.proceed()方法,则修改的参数值不会生效,必须调用joinPoint.proceed(Object[] args)
 17         Object result = joinPoint.proceed();
 18 
 19         // 第一步、获取返回值类型
 20         Class returnType = ((MethodSignature) joinPoint.getSignature()).getReturnType();
 21 
 22         //首先,取出要翻译字段的字典值
 23         String returnJsonResult = JSONObject.toJSONString(result);
 24         //开始解析(翻译字段注解参数指定的字段)
 25         Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
 26         //获取注解上参数
 27         TranslationDict annotation = method.getAnnotation(TranslationDict.class);
 28         FieldParam[] fieldParams = annotation.value();
 29         //遍历
 30         for (FieldParam fieldParam : fieldParams) {
 31             log.info("开始翻译字典CODE:{},取值字段:{},取值字段值类型:{}.",
 32                     fieldParam.dictType(),fieldParam.targetField(),fieldParam.targetFieldValueClazz());
 33             Pattern dictPattern = getPattern(fieldParam);
 34             Matcher dictMatcher=dictPattern.matcher(returnJsonResult);
 35             StringBuffer sb = new StringBuffer();
 36             //转义字段
 37             this.translateDict(fieldParam,dictPattern,dictMatcher,sb);
 38             dictMatcher.appendTail(sb);
 39             returnJsonResult = sb.toString();
 40         }
 41 
 42         result = JSONObject.parseObject(returnJsonResult,returnType);
 43         //如果这里不返回result,则目标对象实际返回值会被置为null
 44         //处理完请求时间
 45         Long endTime = System.currentTimeMillis();
 46         log.info("The request takes {}ms",endTime-startTime);
 47         return result;
 48     }
 49   /**
 50      * 字典值转义为中文
 51      * @param fieldParam
 52      * @param fieldPattern
 53      * @param fieldMatcher
 54      * @param sb
 55      */
 56     private void translateDict(FieldParam fieldParam, Pattern fieldPattern, Matcher fieldMatcher, StringBuffer sb) {
 57         //从缓存中一次性取值
 58         Map<String, String> dictNames = DictData.getDictNames(fieldParam.dictType());
 59         while (fieldMatcher.find()){
 60 
 61             //取出要翻译字段对应的值
 62             Matcher dictValueMatcher = fieldPattern.matcher(fieldMatcher.group());
 63             dictValueMatcher.find();
 64             String group = dictValueMatcher.group();
 65             //""sex":1", ""sex":"1"",""sex":null"
 66             //属性无值
 67             if (group.split(":").length <= 1) continue;
 68             String dictName = "";
 69 
 70             //获取字典值
 71             String dictValue = group.split(":")[1].replace("\"", "");
 72             //属性值非为空 为空赋值空串
 73             if (StringUtils.isNotBlank(dictValue) && !dictValue.toLowerCase().equals("null")){
 74                 //多值
 75                 if (dictValue.split(",").length > 1){
 76                     for (String s : dictValue.split(",")) {
 77                         //fieldParam.dictType() + "_" + s 根据自己字典表设置的规则去查询
 78                         dictName += dictNames.get(fieldParam.dictType() + "_" + s) + "/";
 79                     }
 80                 }else {
 81                     dictName = dictNames.get(fieldParam.dictType() + "_" + dictValue);
 82                 }
 83             }
 84 
 85             String s =  "\"" +  fieldParam.targetField() + "Str" + "\":\"" + dictName + "\"," + fieldMatcher.group();
 86             log.debug("拼接后字符串:{}",s);
 87             fieldMatcher.appendReplacement(sb, s);
 88         }
 89     }
 90    /**
 91      * 获取对应的正则式
 92      * @param fieldParam
 93      * @return
 94      */
 95     private Pattern getPattern(FieldParam fieldParam) {
 96         Pattern fieldPattern;//属性整型 字符型
 97         if (fieldParam.targetFieldValueClazz().equals(Integer.class) ){
 98             fieldPattern= Pattern.compile("\""+fieldParam.targetField() +"\":(\\d+)?");
 99         }else {
100             fieldPattern= Pattern.compile("\""+fieldParam.targetField() +"\":\"([0-9a-zA-Z_,]+)?\"");
101         }
102         return fieldPattern;
103     }
104 }

四、测试

  测试类

 1 @Slf4j
 2 @RestController
 3 @RequestMapping("/demo")
 4 @Api(tags="demo")
 5 public class DemoController {
 6 
 7 
 8     /**
 9      * 测试注解字典
10      * @return
11      */
12     @TranslationDict({@FieldParam(dictType = "CUSTOMER_SEX", targetField = "sex"),
13             @FieldParam(dictType = "CUSTOMER_STATUS", targetField = "status", targetFieldValueClazz = Integer.class)})
14     @GetMapping("/test")
15     @ApiOperation(value = "测试字典转义")
16     public ResultVo test1() {
17         //接收到请求时间
18         Long startTime = System.currentTimeMillis();
19         List result = this.getResult();
20         //处理完请求时间
21         Long endTime = System.currentTimeMillis();
22         log.info("The request takes {}ms",endTime-startTime);
23         return new ResultVo().success(result);
24     }
25 
26     private List getResult() {
27         List demos = new ArrayList<>();
28         Demo demo = new Demo("张三","1,2",1);
29         Demo demo2= new Demo("李四","2,1",2);
30         demos.add(demo);
31         demos.add(demo2);
32 
33         for (int i = 0; i < 5; i++) {
34             demos.add(new Demo("张三"+i,"1",1) );
35         }
36         return demos;
37     }

实体对象

 1 @Data
 2 public class Demo {
 3 
 4     private String name;
 5 
 6     private String sex;
 7 
 8     private Integer status;
 9 
10     public Demo() {
11     }
12 
13     public Demo(String name, String sex, Integer status) {
14         this.name = name;
15         this.sex = sex;
16         this.status = status;
17     }
18 
19     public Demo(String name) {
20         this.name = name;
21     }
22 
23     public Demo(String name, String sex) {
24         this.name = name;
25         this.sex = sex;
26     }
27     
28 }

测试效果

{
  "code": 0,
  "message": "success",
  "data": [
    {
      "statusStr": "报备",
      "sex": "1,2",
      "name": "张三",
      "sexStr": "男/女/",
      "status": 1
    },
    {
      "statusStr": "到访",
      "sex": "2,1",
      "name": "李四",
      "sexStr": "女/男/",
      "status": 2
    },
    {
      "statusStr": "报备",
      "sex": "1",
      "name": "张三0",
      "sexStr": "男",
      "status": 1
    },...
  ]
}

到此本文结束,如您有更好的解决方案,还请留言告知,非常感谢。

参考资料://blog.csdn.net/qq_44754081/article/details/106142458

     //blog.csdn.net/Better_Mei/article/details/103901273

     //my.oschina.net/angelbo/blog/2875887