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

    转自:http://harttle.com/2015/11/06/tmux-startup.html 你是否曾经开过一大堆的Terminal?有没有把它们都保存下来的冲动?Tmux 的Session就 ...

  2. Form-OCR & CSDNAPP初体验

    项目 内容 课程:北航2020春软件工程 博客园班级博客 作业:阅读并撰写博客回答问题 软件案例分析 我在这个课程的目标是 全面地评价一个软件 这个作业在哪个具体方面帮助我实现目标 明确软件开发过程中 ...

  3. Spring 中使用了那些设计模式呢?

    一. Spring 中常见的设计模式 工厂模式 : BeanFactory 装饰器模式: BeanWrapper 代理模式: AopProxy 单例模式: ApplicationContext 委派模 ...

  4. pip安装模块或者更新出现问题Error:Could not install packages due to an EnvironmentError

    问题分析 出现此问题大致的原因: 就是包安装的位置没有读写的权限,这个多半是因为安装python的时候安装在了C盘,或者其他programs这类的文件夹里 或者就是环境变量的设置的安装位置的问题,导致 ...

  5. 无连接运输:UDP

    多路复用和解复用与校验和是UDP唯一能做的事,运输层的协议必须做点什么,什么都没有就不需要这一层了. 为什么要使用UDP 既然有了可靠传输的TCP,为什么还要在udp之上来构件应用呢? 有效载荷大,T ...

  6. Gateway导航

    简介 最近都在弄微服务的东西,现在来记录下收获.我从一知半解到现在能从0搭建使用最大的感触有两点 1.微服务各大组件的版本很多,网上很多博客内容不一定适合你的版本,很多时候苦苦琢磨都是无用功 2.网上 ...

  7. Hutool :一个小而全的 Java 工具类库

    Hutool 简介 Hutool 是一个小而全的 Java 工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以"甜甜的 ...

  8. STM32串口编程易错点

    注意   串口发送函数 使用STM官方的LIB 中的库函数发送之后 加一点延时   否则会错误  接收数据不正常 正确做法是  加上等待发送完成

  9. 10.16-17 mailq&mail:显示邮件传输队列&发送邮件

    mailq命令 是mail queue(邮件队列)的缩写,它会显示待发送的邮件队列,显示的条目包括邮件队列ID.邮件大小.加入队列时间.邮件发送者和接受者.如果邮件进行最后一次尝试后还没有将邮件投递出 ...

  10. 如何正确地使用RecyclerView.ListAdapter

    默认是在一个fragment中实现RecyclerView. private inner class CrimeAdapter() : ListAdapter<Crime, CrimeHolde ...