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

基于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. js取随机数看这里

    取0~10的随机数 Math.Random()*10 ; 取1~10的随机数 Math.Random()*9 + 1 ; 取0~10的随机整数(十一个数字) Math.floor( Math.Rand ...

  2. 老vue项目webpack3升级到webpack5全过程记录(一)

    背景 19年新建的vue项目,使用的是webpack3,随着项目的积累,组件的增多导致本地构建,线上打包等操作速度极慢,非常影响开发效率和部署效率,基于此问题,本次对webpack及相关插件进行了优化 ...

  3. R语言执行脚本的几种命令

    R CMD BATCH 和 Rscript 使用前都要先添加环境变量 把 C:\Program Files\R\R-3.3.0\bin; 加到"系统变量"的Path 值的最开始 可 ...

  4. 运维实战案例之“Too many open files”错误与解决方法

    运维实战案例之"Too many open files"错误与解决方法   技术小甜 2017-11-16 15:02:00 浏览869 服务器 shell tomcat 脚本 o ...

  5. ffmpeg安装之linux编译安装

    转发自白狼栈:查看原文 关于ffmpeg的安装,有的人可能要折腾很久,甚至折腾一个礼拜,究其原因,基本都是编译安装惹的祸. 我们提供4种安装方式,最复杂的莫过于centos7上的编译安装. ffmpe ...

  6. ThreadLocal与ThreadLocalMap源码分析

    ThreadLocal类 该类主要用于不同线程存储自己的线程本地变量.本文先通过一个示例简单介绍该类的使用方法,然后从ThreadLocal类的初始化.存储结构.增删数据和hash值计算等几个方面,分 ...

  7. 使用Jekyll + GitHub Pages免费搭建个人博客

    使用Jekyll + GitHub Pages免费搭建个人博客 My Blog:无名の辈 | VectorX (vectorxxxx.github.io) Download Ruby:Download ...

  8. 004:ZYNQ_AXI总线学习笔记(1)

    1.    WHAT IS AXI? AXI是一种高级可扩展接口,是ARM AMBA的一部分. 2.    WHAT IS AMBA? AMBA是高级微控制器总线架构,开放的片内互联总线标准. 3.A ...

  9. V $ BACKUP_DATAFILE

    V$BACKUP_DATAFILE 从控制文件显示有关备份集中的控制文件和数据文件的信息. 柱 数据类型 描述 RECID NUMBER 备份数据文件记录ID STAMP NUMBER 备份数据文件记 ...

  10. Timer定时器开发

    Timer定时器开发 定时器的作用是不占线程的等待一个确定时间,同样通过callback来通知定时器到期. 参考:https://github.com/sogou/workflow 定时器的创建 同样 ...