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

    前言 日常开发中,我们经常会碰到这样的业务场景:用户注册,注册成功后需要发送邮箱.短信提示用户,通常我们都是这样写: /** * 用户注册 */ @GetMapping("/userRegi ...

  2. Flink使用二次聚合实现TopN计算-乱序数据

    一.背景说明: 在上篇文章实现了TopN计算,但是碰到迟到数据则会无法在当前窗口计算,需要对其中的键控状态优化 Flink使用二次聚合实现TopN计算 本次需求是对数据进行统计,要求每隔5秒,输出最近 ...

  3. [刷题] 435 Non-overlapping Intervals

    要求 贪心算法与动态规划的关系 给定一组区间,最少删除多少个区间,可以让这些区间之间互相不重叠 给定区间的起始点永远小于终止点 示例 [[1,2],[2,3],[3,4],[1,3]], 返回1 [[ ...

  4. scala :: , +:, :+ , ::: , ++ 的区别

    4 种操作符的区别和联系 :: 该方法被称为cons,意为构造,向队列的头部追加数据,创造新的列表.用法为 x::list,其中x为加入到头部的元素,无论x是列表与否,它都只将成为新生成列表的第一个元 ...

  5. linux 服务开机自启动systemd方式 (Centos7)

    linux 服务开机自启动systemd方式 (Centos7) 1.编写一个 /opt/hello.sh 脚本 [root@jws-ftp prometheus]# cat /opt/hello.s ...

  6. shell基础之变量及表达式

    本节内容 1. shell变量简介 2. 定义变量 3. 使用变量 4. 修改变量的值 5. 单引号和双引号的区别 6. 将命令的结果赋值给变量 7. 删除变量 8. 变量类型 9. 特殊变量列表 1 ...

  7. Go语言实现Snowflake雪花算法

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com/archives/527 每次放长假的在家里的时候,总想找点简单的例子来看看实现原理,这 ...

  8. Step By Step(Lua-C API简介)

    Step By Step(Lua-C API简介) Lua是一种嵌入式脚本语言,即Lua不是可以单独运行的程序,在实际应用中,主要存在两种应用形式.第一种形式是,C/C++作为主程序,调用Lua代码, ...

  9. conda 按照指定源下载python包

    conda 按照指定源下载python包 换成了国内的pip源就可以正常安装了,我使用的是:pip install xlrd -i http://pypi.douban.com/simple --tr ...

  10. 【注意力机制】Attention Augmented Convolutional Networks

    注意力机制之Attention Augmented Convolutional Networks 原始链接:https://www.yuque.com/lart/papers/aaconv 核心内容 ...