基于SpringBoot 、AOP与自定义注解转义字典值
一直以来,前端展示字典一般以中文展示为主,若在表中存字典值中文,当字典表更改字典值对应的中文,会造成数据不一致,为此设置冗余字段并非最优方案,若由前端自己写死转义,不够灵活,若在业务代码转义,臃肿也不够通用,从网络上了解到注解、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与自定义注解转义字典值的更多相关文章
- 基于SpringBoot AOP面向切面编程实现Redis分布式锁
基于SpringBoot AOP面向切面编程实现Redis分布式锁 基于SpringBoot AOP面向切面编程实现Redis分布式锁 基于SpringBoot AOP面向切面编程实现Redis分布式 ...
- 用AOP拦截自定义注解并获取注解属性与上下文参数(基于Springboot框架)
目录 自定义注解 定义切面 获取上下文信息JoinPoint ProceedingJoinPoint 定义测试方法 测试结果 小结 AOP可以用于日志的设计,这样话就少不了要获取上下文的信息,博主在设 ...
- 【spring boot】SpringBoot初学(6)– aop与自定义注解
前言 github: https://github.com/vergilyn/SpringBootDemo 一.AOP 官方demo:https://github.com/spring-project ...
- springboot通过AOP和自定义注解实现权限校验
自定义注解 PermissionCheck: package com.mgdd.sys.annotation; import java.lang.annotation.*; /** * @author ...
- spring AOP 和自定义注解进行身份验证
一个SSH的项目(springmvc+hibernate),需要提供接口给app使用.首先考虑的就是权限问题,app要遵循极简模式,部分内容无需验证,用过滤器不能解决某些无需验证的方法 所以最终选择用 ...
- Spring Boot系列——AOP配自定义注解的最佳实践
AOP(Aspect Oriented Programming),即面向切面编程,是Spring框架的大杀器之一. 首先,我声明下,我不是来系统介绍什么是AOP,更不是照本宣科讲解什么是连接点.切面. ...
- AOP 实现自定义注解
1.自定义注解2.编写 AOP3.测试 1.自定义注解 package com.base.yun.spring.aop; import java.lang.annotation.Documented; ...
- SpringBoot+SpringAOP+Java自定义注解+mybatis实现切库读写分离
一.定义我们自己的切库注解类 自定义注解有几点需要注意: 1)@Target 是作用的目标,接口.方法.类.字段.包等等,具体看:ElementType 2)@Retention 是注解存在的范围,R ...
- 使用AOP获取自定义注解的内容
目录结构: 一:自定义注解 package org.example.annotation; import java.lang.annotation.ElementType; import java.l ...
随机推荐
- 03.14 ICPC训练联盟周赛,Preliminaries for Benelux Algorithm Programming Contest 2019
A .Architecture 题意:其实就是想让你找到两行数的最大值,然后比较是否相同,如果相同输出'possible',不同则输出'impossible' 思路:直接遍历寻找最大值,然后比较即可 ...
- Java项目中每一个类都可以有一个main方法
Java项目中每一个类都可以有一个main方法,但只有一个main方法会被执行,其他main方法可以对类进行单元测试. public class StaticTest { public static ...
- Pytorch_Part5_迭代训练
VisualPytorch beta发布了! 功能概述:通过可视化拖拽网络层方式搭建模型,可选择不同数据集.损失函数.优化器生成可运行pytorch代码 扩展功能:1. 模型搭建支持模块的嵌套:2. ...
- 重新整理 .net core 实践篇————依赖注入应用[二]
前言 这里介绍一下.net core的依赖注入框架,其中其代码原理在我的另一个整理<<重新整理 1400篇>>中已经写了,故而专门整理应用这一块. 以下只是个人整理,如有问题, ...
- [bug] CDH报错:cloudera-scm-server dead but pid file exists
参考 https://blog.csdn.net/levy_cui/article/details/51243335
- [bug] Maven项目缺少Maven Dependencies
参考 https://blog.csdn.net/whitemiao/article/details/90177135
- 011.Kubernetes使用共享存储持久化数据
本次实验是以前面的实验为基础,使用的是模拟使用kubernetes集群部署一个企业版的wordpress为实例进行研究学习,主要的过程如下: 1.mysql deployment部署, wordpre ...
- Linux进阶之进程管理
本节内容 1.进程管理 2.ps 3.uptime 4.top 5.ss -tnl------ lsof -i :22 一. 进程管理的概念 程序:二进制文件,静态 /bin/date,/usr/sb ...
- ASP.NET Core MVC 入门到精通 - 3. 使用MediatR
ASP.NET Core MVC 入门到精通 - 3. 使用MediatR 环境: .NET 5 ASP.NET Core MVC (project) 1. MediatR MediatR .NET中 ...
- .NET5 WPF进阶教程
一.概要 本系列将继<.net wpf快速入门教程>带领大家了解wpf,帮助各位在初级向中级过渡的中掌握基本该具备的能力.本系列视频长度大约在15分钟到30分钟左右,视频内容不仅仅会讲解技 ...