一般来说,项目开发会用自定义注解去实现日志监控等操作
在实际项目中,前后台数据交互时,每次都需要根据一个code值去进行查询数据库进行中间操作进行获取text值
本博文用自定义注解结合aop实现数据字典的自动翻译
首先附上数据库表结构:
数据字典类型中间表(dict_type)
用户表(user)
注解类(Dict)
package com.xiaoyang.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author xiaoyang
* @create 2020-11-27 10:36
* 数据字典的数字转汉字的自定义注解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Dict {
/**
* 数据dataSource
*
* @return
*/
String dictDataSource();
/**
* 返回put到json中的文本key
* @return
*/
String dictText() default "";
}
Controller:
package com.xiaoyang.controller;
import com.xiaoyang.model.User;
import com.xiaoyang.service.UserService;
import com.xiaoyang.util.PageBean;
import com.xiaoyang.util.PageUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/**
* @author xiaoyang
* @create 2020-11-27 16:47
*/
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@ResponseBody
@RequestMapping("/QueryPager")
public PageUtils QueryPager(HttpServletRequest req) {
User user=new User();
user.setName("a");
PageBean pageBean = new PageBean();
pageBean.setRequest(req);
List<User> users = this.userService.QueryPager(user, pageBean);
PageUtils pageUtils = new PageUtils(users, pageBean.getTotal());
return pageUtils;
}
}
aop切面类:
package com.xiaoyang;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaoyang.annotation.Dict;
import com.xiaoyang.service.DictService;
import com.xiaoyang.util.ObjConvertUtils;
import com.xiaoyang.util.PageUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
@author xiaoyang
@create 2020-11-27 16:52
*/
@Aspect
@Component
@Slf4j
public class DictAspect {
//这是操作数据字典那张表的 service
@Autowired
private DictService dictService;
//翻译后拼接的内容
private static String DICT_TEXT_SUFFIX = "_dictText";
// 定义切点Pointcut 拦截所有对服务器的请求
@Pointcut("execution( * com.xiaoyang..controller.*.*(..))")
public void excudeService() {
}
/**
* 这是触发 excudeService 的时候会执行的
*
* @param pjp
* @return
* @throws Throwable
*/
@Around("excudeService()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
//这是定义开始事件
long time1 = System.currentTimeMillis();
//这是方法并获取返回结果
Object result = pjp.proceed();
//这是获取到 结束时间
long time2 = System.currentTimeMillis();
log.debug("获取JSON数据 耗时:" + (time2 - time1) + "ms");
//解析开始时间
long start = System.currentTimeMillis();
//开始解析(翻译字段内部的值凡是打了 @Dict 这玩意的都会被翻译)
this.parseDictText(result);
//解析结束时间
long end = System.currentTimeMillis();
log.debug("解析注入JSON数据 耗时" + (end - start) + "ms");
return result;
}
/**
* 本方法针对返回对象为Result 的IPage的分页列表数据进行动态字典注入
* 字典注入实现 通过对实体类添加注解@dict 来标识需要的字典内容,字典分为单字典code即可 ,table字典 code table text配合使用与原来jeecg的用法相同
* 示例为SysUser 字段为sex 添加了注解@Dict(dicCode = "sex") 会在字典服务立马查出来对应的text 然后在请求list的时候将这个字典text,已字段名称加_dictText形式返回到前端
* 例输入当前返回值的就会多出一个sex_dictText字段
* {
* sex:1,
* sex_dictText:"男"
* }
* 前端直接取值sext_dictText在table里面无需再进行前端的字典转换了
* customRender:function (text) {
* if(text==1){
* return "男";
* }else if(text==2){
* return "女";
* }else{
* return text;
* }
* }
* 目前vue是这么进行字典渲染到table上的多了就很麻烦了 这个直接在服务端渲染完成前端可以直接用
*
* @param result
*/
private void parseDictText(Object result) {
if (result instanceof PageUtils) {
List<JSONObject> items = new ArrayList<>();
PageUtils pageUtils = (PageUtils) result;
//循环查找出来的数据
for (Object record : pageUtils.getData()) {
ObjectMapper mapper = new ObjectMapper();
String json = "{}";
try {
//解决@JsonFormat注解解析不了的问题详见SysAnnouncement类的@JsonFormat
json = mapper.writeValueAsString(record);
} catch (JsonProcessingException e) {
log.error("json解析失败" + e.getMessage(), e);
}
JSONObject item = JSONObject.parseObject(json);
//update-begin--Author:scott -- Date:20190603 ----for:解决继承实体字段无法翻译问题------
//for (Field field : record.getClass().getDeclaredFields()) {
for (Field field : ObjConvertUtils.getAllFields(record)) {
//update-end--Author:scott -- Date:20190603 ----for:解决继承实体字段无法翻译问题------
if (field.getAnnotation(Dict.class) != null) {
String code = field.getAnnotation(Dict.class).dictDataSource();
String text = field.getAnnotation(Dict.class).dictText();
//获取当前带翻译的值
String key = String.valueOf(item.get(field.getName()));
//翻译字典值对应的txt
String textValue = translateDictValue(code, key);
// CommonConstant.DICT_TEXT_SUFFIX的值为,是默认值:
// public static final String DICT_TEXT_SUFFIX = "_dictText";
log.debug(" 字典Val : " + textValue);
log.debug(" __翻译字典字段__ " + field.getName() + DICT_TEXT_SUFFIX + ": " + textValue);
//如果给了文本名
if (!StringUtils.isEmpty(text)) {
item.put(text, textValue);
} else {
//走默认策略
item.put(field.getName() + DICT_TEXT_SUFFIX, textValue);
}
}
//date类型默认转换string格式化日期
if (field.getType().getName().equals("java.util.Date") && field.getAnnotation(JsonFormat.class) == null && item.get(field.getName()) != null) {
SimpleDateFormat aDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
item.put(field.getName(), aDate.format(new Date((Long) item.get(field.getName()))));
}
}
items.add(item);
}
pageUtils.setData(items);
}
}
/**
* 翻译字典文本
*
* @param code
* @param key
* @return
*/
private String translateDictValue(String code, String key) {
//如果key为空直接返回就好了
if (ObjConvertUtils.isEmpty(key)) {
return null;
}
StringBuffer textValue = new StringBuffer();
//分割 key 值
System.out.println(code+":::::"+key);
String[] keys = key.split(",");
//循环 keys 中的所有值
for (String k : keys) {
String tmpValue = null;
log.debug(" 字典 key : " + k);
if (k.trim().length() == 0) {
continue; //跳过循环
}
tmpValue = dictService.selectByDatasourceCode(code,k.trim());
if (tmpValue != null) {
if (!"".equals(textValue.toString())) {
textValue.append(",");
}
textValue.append(tmpValue);
}
}
//返回翻译的值
return textValue.toString();
}
}
工具类(反射类):
package com.xiaoyang.util;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author xiaoyang
* @create 2020-11-29 8:32
*/
public class ObjConvertUtils {
/**
* 获取类的所有属性,包括父类
*
* @param object
* @return
*/
public static Field[] getAllFields(Object object) {
Class<?> clazz = object.getClass();
List<Field> fieldList = new ArrayList<>();
while (clazz != null) {
fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
clazz = clazz.getSuperclass();
}
Field[] fields = new Field[fieldList.size()];
fieldList.toArray(fields);
return fields;
}
public static boolean isEmpty(Object object) {
if (object == null) {
return (true);
}
if ("".equals(object)) {
return (true);
}
if ("null".equals(object)) {
return (true);
}
return (false);
}
}
在User
的实体类中使用注解(可传两个参数,这里使用注解后会在切面类进行获取注解的内容进行数据库查询,例如这里userlevel
属性注解传入的是user_level
,那么controller层查询出结果集合后,在切面类进行字典表对应字段的查询):
结果:
自定义注解运用aop的思想去进行数据字典的转译从根本上来说就是为了简化我们的代码,实现后不需要每次都调用方法去查询(需结合缓存)
最后附上流程图供理解:
over…