基於SpringBoot 、AOP與自定義註解轉義字典值
- 2021 年 6 月 29 日
- 筆記
- springboot, 字典轉義
一直以來,前端展示字典一般以中文展示為主,若在表中存字典值中文,當字典表更改字典值對應的中文,會造成數據不一致,為此設置冗餘欄位並非最優方案,若由前端自己寫死轉義,不夠靈活,若在業務程式碼轉義,臃腫也不夠通用,從網路上了解到註解、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