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

    1. 返回某范围的随机数 Random.Range method. static float Range(float min, float max); returns a float – return ...

  2. CRM系统全方位管理企业

    您在选择一款CRM系统的时候,首先要考虑销售团队的感受和意见.让CRM系统在帮助销售团队优化工作流程的同时,更好地对销售团队进行管理.销售人员每卖出一件商品,要从寻找筛选商机开始,经过沟通客户需求.满 ...

  3. 【vue2】(一)基础使用

    [vue2](一)基础使用 MVVM MVVM: View - Model - ViewModel View: Dom层,视图层 Model: Plain JavaScript Objects,数据层 ...

  4. jQuery的入口和jQurey的对象切换

    jQuery jQuery的导入 通过script标签的src属性,link标签是导入层叠样式表 jQuery和原生JS的入口函数 1.jQ在页面结构加载完毕就会执行 原生JS的入口函数是等图片.层叠 ...

  5. Python批量图片去水印,提高工作效率

    ​平常工作中,有时为了采用网络的一些素材,但这些素材往往被打了水印,如果我们不懂PS就无法去掉水印,或者无法批量去掉水印.这些就很影响我们的工作效率. 今天我们就一起来,用Python + OpenC ...

  6. [刷题] 206 Reverse Linked List

    要求 反转一个链表 不得改变节点的值 示例 head->1->2->3->4->5->NULL NULL<-1<-2<-3<-4<-5 ...

  7. 利用rsync备份生产应用(一)

    rsync简单介绍 Rsync(remote synchronize)是一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件.Rsync使用所谓的"Rsync算法"来 ...

  8. Ubuntu 20.04 版本安装

    Ubuntu 20.04 版本安装 安装步骤 首先创建好Ubuntu 20.04虚拟机 等待系统检查完整性 选择语言 选择不更新,回车确定 键盘语言默认即可 网卡IP配置 设置代理服务器 设置源 自定 ...

  9. 安装oracle 时“[INS-30014]无法检查指定的位置是否位于 CFS上”问题

    错误截图: 错误信息: [INS-30014]无法检查指定的位置是否位于 CFS上 解决方案: 通过修改hosts文件,向C:\Windows\System32\drivers\etc\hosts文件 ...

  10. clickhouse_各个引擎表的特点

    去重表:RepalcingMergeTree 1.使用order by排序键作为判断重复数据的唯一键 2.只有在合并分区的时候才会触发删除重复数据的逻辑 3.以数据分区为单位删除重复数据.当分区合并时 ...