基於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