说明

在前后端分离开发的场景下,后端经常会需要将状态或者类型这样的数据以阿拉伯数字返回,比如1:生效,2:失效;3:发布中等等;以往我们的做法都是前后端沟通好前端通过数字和中文自己对应并显示。后端如果修改字典前端也需要一起跟着改

字典回显插件主要解决上述问题,在返回状态数字时候通过一个注解自动将数字对应的中文标识返回前端

原理

  1. 为了实现引用及能使用的原则,借助SpringBoot自动配置原理
  2. 通过AOP拦截系统返回的JSON参数,根据类型注入字典值(目前支持data的返回格式为:数组,分页、对象、对象中嵌套list)
  3. 通过Mybatis返回数据增加字典数据绑定;

实现

一、自动配置

在resources->META-INF->新建spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.yangxc.common.dict.DictAutoConfiguration

DictAutoConfiguration对应的代码片段

@Configuration
@RequiredArgsConstructor
public class DictAutoConfiguration {@Beanpublic DictAspect dictAspect(){return new DictAspect();}@Beanpublic DictInterceptor dictInterceptor() {return new DictInterceptor();}}

二、相关注解

此处提供两种方式,AOP和Mybatis,提供如下注解

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DictTransByAop {/*** 字典的type类型,为空时默认为当前属性名* @return*/String type() default "";/*** 目标属性名,需要同时增加属性* 例如:target="sexDesc" 需增加属性* private String sexDesc;* @return*/String target() default "";
}

Mybatis方式:

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DictTransByMybatis {String type() default "";String target() default "";}

是否启用字典回显注解,为了提高系统性能只有配置开启了字典回显功能的才处理相关逻辑

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface NeedReturnDict {}

三、逻辑实现

AOP方式切面逻辑实现,通过拦截NeedReturnDict注解,解析返回参数中标注了DictTransByAop注解的字段根据声明的key获取字典中的值,赋值给desc:

@Slf4j
@Aspect
@SuppressWarnings({"unused"})
public class DictAspect {@Resourceprivate DictCache dictCache;private volatile Map<String, Object> dictInfoMap = new ConcurrentHashMap<>();@Around("@annotation(needReturnDict)")public Object translation(final ProceedingJoinPoint pjp, NeedReturnDict needReturnDict) throws Throwable {//目标方法执行Object resultR = pjp.proceed();if (ObjectUtil.isNull(resultR)) {return resultR;}//获取返回data值Object result = ((R) resultR).getData();if (result instanceof IPage) {// 分页的情况IPage page = (IPage) result;result = ((IPage) result).getRecords();result = translate(result);page.setRecords((List) result);((R) resultR).setData(page);return resultR;}result = translate(result);((R) resultR).setData(result);return resultR;}/*** 返回值转换,增加字典回显* @param result* @return*/private Object translate(Object result) {if (result instanceof List || result instanceof ArrayList) {List<JSONObject> items = new ArrayList<>();for (Object entity : (List) result) {to(entity);}} else {to(result);}return result;}/*** 根据类上注解,设置目标属性值* @param entity 返回对象*/public void to(Object entity) {Class c = entity.getClass();for (; c != Object.class; c = c.getSuperclass()){try {Field[] fields = c.getDeclaredFields();for (Field field : fields){field.setAccessible(true);Object preValue = field.get(entity);Class<?> type = field.getType();if (ObjectUtil.isNotNull(preValue)) {//如果对象中包含list,判断list中是否包含注解if (type.equals(List.class) || type.equals(ArrayList.class)) {// 当前集合的泛型类型Type genericType = field.getGenericType();if (null == genericType) {continue;}if (genericType instanceof ParameterizedType) {for (Object o : (List) preValue) {to(o);}}}/*else if(type.isArray()){Object[] objArr = (Object[]) preValue;if (objArr != null && objArr.length > 0) {for (Object arr : objArr) {to(arr);}}}*///todo 自定义对象方式if (field.isAnnotationPresent(DictTransByAop.class)) {DictTransByAop dictTranslation = field.getAnnotation(DictTransByAop.class);// 需要赋值的字段Field fvalue = c.getDeclaredField(dictTranslation.target());fvalue.setAccessible(true);//当前属性名String currentFiledName = StrUtil.isEmpty(dictTranslation.type())? field.getName() : dictTranslation.type();Object dictValue = dictCache.get(currentFiledName,preValue);//设置字典内容fvalue.set(entity, dictValue);}}}} catch (NoSuchFieldException e){//父类存在子类不存在情况}catch (Exception e) {log.error("字典回显失败:{}", JSONUtil.toJsonStr(entity));e.printStackTrace();}}}}

Mybatis拦截器实现方式,数据库返回数据后去给相关字段赋值

@Intercepts({ @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = { Statement.class }) })
public class DictInterceptor implements Interceptor {@Resourceprivate DictCache dictCache;@Overridepublic Object intercept(Invocation invocation) throws Throwable {return setFieldValue(invocation.proceed());}@Overridepublic Object plugin(Object target) {if (target instanceof ResultSetHandler) {return Plugin.wrap(target, this);}return target;}@Overridepublic void setProperties(Properties properties) {}private Object setFieldValue(Object result){if (result instanceof Collection) {List<Object> list = new ArrayList<>();for (Object obj : (Collection)result) {list.add(setFieldValue(obj));}return list;}// 此处可根据需要加上一些判断优化处理,如result为基本数据类型对象、Map时不再执行后续代码,或只处理限定类等Field[] fs = ReflectUtil.getFields(result.getClass());for (Field f : fs) {DictTransByMybatis dictTransByMybatis = f.getAnnotation(DictTransByMybatis.class);if (dictTransByMybatis == null) {continue;}// 获取@DictTransByMybatis标记字段具体值Object value = ReflectUtil.getFieldValue(result, f);//当前属性名String currentFiledName = StrUtil.isEmpty(dictTransByMybatis.type())? f.getName() : dictTransByMybatis.type();Object dictValue = dictCache.get(currentFiledName,value);// 设置目标字段的转换值ReflectUtil.setFieldValue(result, dictTransByMybatis.target(), dictValue);}return result;}}

上述dictCache为一个查询字典内容的扩展接口,大家可以根据自己系统自定义实现、接口定义如下:

public interface DictCache {/*** 获取所有缓存* @return*/public Map<String, Object> getAll();/*** 根据字典类型,字典值获取字典* @param type* @param value* @return*/public Object get(String type,Object value);/*** 保存字典* @param type* @param map*/public void save(String type,Map<String, Object> map);/*** 清空缓存*/public void removeDict();
}

总结

大家可以根据此原理封装为自己的starter,直接pom引入使用。


获取更多相关文章请点击木易成-开发者天地,不定时分享个人技术观点、股票交易经验、生活经历、NBA观点等精彩信息。

Springboot字典回显相关推荐

  1. SpringBoot 2.0 多图片上传加回显

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 本文来源:http://r6f.cn/crEY 这两天公司 ...

  2. java spring 上传图片,springboot 上传图片并回显

    之前也有做过上传图片的功能,不过是用在ssm的项目中,也有很多的不完美. 这次用的springboot,基本上对上传图片又有了一定的认识,想再这里记录一下./** * 上传图片 * * @return ...

  3. SpringBoot+MyBatisPlus+DataTables实现退货管理的添加和编辑时控制checkbox的回显选中

    场景 SpringBoot+My BatisPlus+DataTables实现企业车间退货管理(学习企业级开发思想): https://blog.csdn.net/BADAO_LIUMANG_QIZH ...

  4. SpringBoot中使用thymeleaf时ajax请求不能回显消息

    场景 在SpringBoot项目中使用thymeleaf模板时,在js文件中使用ajax提交表单 不能成功回显消息. 实现 修改为 html中: <button id="parseBt ...

  5. themyleaf 图片上传_javaEE --springboot #实现图片上传和回显 #单文件上传 #多文件上传 #ajax异步文件上传 (非常详细,从创建项目开始)...

    实现文件上传和回显 1.新建一个SpringBoot项目,选择 Spring Web 和 thymeleaf 依赖 .pow.xml文件下的依赖如下 2.根据下图,创建如下文件 3.直接上代码 配置文 ...

  6. springboot+thymeleaf模糊查询功能和分页实现以及input前端回显(前台到后台)

    先放图 实体类 import lombok.Data;@Data public class Word {private Integer wordId;private String wordName;p ...

  7. springboot 上传图片并回显

    之前也有做过上传图片的功能,不过是用在ssm的项目中,也有很多的不完美. 这次用的springboot,基本上对上传图片又有了一定的认识,想再这里记录一下. /*** 上传图片** @return*/ ...

  8. 【SpringBoot】简单的文件上传和文件下载以及图片回显

    介绍 这里是小编成长之路的历程,也是小编的学习之路.希望和各位大佬们一起成长! 以下为小编最喜欢的两句话: 要有最朴素的生活和最遥远的梦想,即使明天天寒地冻,山高水远,路远马亡. 一个人为什么要努力? ...

  9. springboot项目上传头像回显

    springboot项目使用ajax上传头像回显 先上效果图: 开始上传 点击提交 环境: springboot2.2.5.RELEASE jdk:1.8 thymeleaf:3.0.4.RELEAS ...

最新文章

  1. google svn 服务器申请 使用
  2. SQL笛卡尔积结合前后行数据的统计案例
  3. 进程、线程与任务程序之间的关系
  4. linux防火墙阻断目的,基于Linux防火墙的内部邮件监控与阻断系统
  5. Software Ate The World, Open Source Is Eating The Software World
  6. hadoop Connection refused: no further information原因排查(Centos7)
  7. android 图片叠加xml,Android实现图片叠加效果的两种方法
  8. DeepMind发布《神经网络中持续学习》Cell综述论文
  9. 2020 比特大陆 面经
  10. Java面试题2.0--solr
  11. iOS 应用内付费(IAP)开发步骤一:填写相关的税务,银行,联系人信息;
  12. 几种常见的4K高清视频信号传输方案对比
  13. 旷视科技 CVPR部分文章
  14. Android在中国的发展及就业前景解析
  15. 文件被占用删除不了?快来我给你一招解决!
  16. 如何对电脑屏幕进行监控?
  17. Autosar BSW开发必知的“术语”+“缩写”概念-1-诊断通信篇
  18. Ubuntu 安装Jupyter Notebook 最基础的操作
  19. 使用scrapy爬取qq音乐
  20. Machine learning K-Means课堂笔记

热门文章

  1. 银联支付api相关文档
  2. 软考中级 真题 2014年上半年 系统集成项目管理工程师 应用技术 下午试卷
  3. 几道web前端面试题
  4. 【A星算法的优化方案】
  5. Android Camera 四 Camera HAL 分析
  6. STM32八种IO口模式区别,以及上拉输入、下拉输入、浮空输入、模拟输入的区别
  7. 前端IM使用以及视频通讯记录分项
  8. 服务器重装ie浏览器,怎么重装IE浏览器
  9. php 请求第三方接口发送短信验证码及注册手机号码
  10. 微信|公众平台开发者文档