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

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

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

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

一、新建注解

  标注方法上使用

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

  注解参数:FieldParam

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

二、注解的使用

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

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

三、新建切面

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

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

四、测试

  测试类

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

实体对象

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

测试效果

  1. {
  2. "code": 0,
  3. "message": "success",
  4. "data": [
  5. {
  6. "statusStr": "报备",
  7. "sex": "1,2",
  8. "name": "张三",
  9. "sexStr": "男/女/",
  10. "status": 1
  11. },
  12. {
  13. "statusStr": "到访",
  14. "sex": "2,1",
  15. "name": "李四",
  16. "sexStr": "女/男/",
  17. "status": 2
  18. },
  19. {
  20. "statusStr": "报备",
  21. "sex": "1",
  22. "name": "张三0",
  23. "sexStr": "男",
  24. "status": 1
  25. },...
  26. ]
  27. }

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

参考资料: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

基于SpringBoot 、AOP与自定义注解转义字典值的更多相关文章

  1. 基于SpringBoot AOP面向切面编程实现Redis分布式锁

    基于SpringBoot AOP面向切面编程实现Redis分布式锁 基于SpringBoot AOP面向切面编程实现Redis分布式锁 基于SpringBoot AOP面向切面编程实现Redis分布式 ...

  2. 用AOP拦截自定义注解并获取注解属性与上下文参数(基于Springboot框架)

    目录 自定义注解 定义切面 获取上下文信息JoinPoint ProceedingJoinPoint 定义测试方法 测试结果 小结 AOP可以用于日志的设计,这样话就少不了要获取上下文的信息,博主在设 ...

  3. 【spring boot】SpringBoot初学(6)– aop与自定义注解

    前言 github: https://github.com/vergilyn/SpringBootDemo 一.AOP 官方demo:https://github.com/spring-project ...

  4. springboot通过AOP和自定义注解实现权限校验

    自定义注解 PermissionCheck: package com.mgdd.sys.annotation; import java.lang.annotation.*; /** * @author ...

  5. spring AOP 和自定义注解进行身份验证

    一个SSH的项目(springmvc+hibernate),需要提供接口给app使用.首先考虑的就是权限问题,app要遵循极简模式,部分内容无需验证,用过滤器不能解决某些无需验证的方法 所以最终选择用 ...

  6. Spring Boot系列——AOP配自定义注解的最佳实践

    AOP(Aspect Oriented Programming),即面向切面编程,是Spring框架的大杀器之一. 首先,我声明下,我不是来系统介绍什么是AOP,更不是照本宣科讲解什么是连接点.切面. ...

  7. AOP 实现自定义注解

    1.自定义注解2.编写 AOP3.测试 1.自定义注解 package com.base.yun.spring.aop; import java.lang.annotation.Documented; ...

  8. SpringBoot+SpringAOP+Java自定义注解+mybatis实现切库读写分离

    一.定义我们自己的切库注解类 自定义注解有几点需要注意: 1)@Target 是作用的目标,接口.方法.类.字段.包等等,具体看:ElementType 2)@Retention 是注解存在的范围,R ...

  9. 使用AOP获取自定义注解的内容

    目录结构: 一:自定义注解 package org.example.annotation; import java.lang.annotation.ElementType; import java.l ...

随机推荐

  1. 03.14 ICPC训练联盟周赛,Preliminaries for Benelux Algorithm Programming Contest 2019

    A .Architecture 题意:其实就是想让你找到两行数的最大值,然后比较是否相同,如果相同输出'possible',不同则输出'impossible' 思路:直接遍历寻找最大值,然后比较即可 ...

  2. Java项目中每一个类都可以有一个main方法

    Java项目中每一个类都可以有一个main方法,但只有一个main方法会被执行,其他main方法可以对类进行单元测试. public class StaticTest { public static ...

  3. Pytorch_Part5_迭代训练

    VisualPytorch beta发布了! 功能概述:通过可视化拖拽网络层方式搭建模型,可选择不同数据集.损失函数.优化器生成可运行pytorch代码 扩展功能:1. 模型搭建支持模块的嵌套:2. ...

  4. 重新整理 .net core 实践篇————依赖注入应用[二]

    前言 这里介绍一下.net core的依赖注入框架,其中其代码原理在我的另一个整理<<重新整理 1400篇>>中已经写了,故而专门整理应用这一块. 以下只是个人整理,如有问题, ...

  5. [bug] CDH报错:cloudera-scm-server dead but pid file exists

    参考 https://blog.csdn.net/levy_cui/article/details/51243335

  6. [bug] Maven项目缺少Maven Dependencies

    参考 https://blog.csdn.net/whitemiao/article/details/90177135

  7. 011.Kubernetes使用共享存储持久化数据

    本次实验是以前面的实验为基础,使用的是模拟使用kubernetes集群部署一个企业版的wordpress为实例进行研究学习,主要的过程如下: 1.mysql deployment部署, wordpre ...

  8. Linux进阶之进程管理

    本节内容 1.进程管理 2.ps 3.uptime 4.top 5.ss -tnl------ lsof -i :22 一. 进程管理的概念 程序:二进制文件,静态 /bin/date,/usr/sb ...

  9. ASP.NET Core MVC 入门到精通 - 3. 使用MediatR

    ASP.NET Core MVC 入门到精通 - 3. 使用MediatR 环境: .NET 5 ASP.NET Core MVC (project) 1. MediatR MediatR .NET中 ...

  10. .NET5 WPF进阶教程

    一.概要 本系列将继<.net wpf快速入门教程>带领大家了解wpf,帮助各位在初级向中级过渡的中掌握基本该具备的能力.本系列视频长度大约在15分钟到30分钟左右,视频内容不仅仅会讲解技 ...