基于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 },... ] }
到此本文结束,如您有更好的解决方案,还请留言告知,非常感谢。
参考资料:https://blog.csdn.net/qq_44754081/article/details/106142458
https://blog.csdn.net/Better_Mei/article/details/103901273
https://my.oschina.net/angelbo/blog/2875887