一:需求背景

	在业务开发中经常会有这个一个场景,A(业务表)表中会记录数据的创建人,通常我们会用userId字段记录该数据的创建者,但数据的使用方会要求展示该数据的创建者姓名,故我们会关联用户表拿该用户的姓名。还有一些枚举值的含义也要展示给前端。导致原本一个单表的sql就要写成多表的关联sql,以及枚举含义的转换很是恶心。

例如:业务对象BusinessEntity.java

public class BusinessEntity {

    /**
* 创建者id
*/
private Long createUserId;
/**
* 创建者名称 (需要关联用户表)
*/
private String userName; /**
* 数据状态(0:有效,1失效)
*/
private String status; /**
* 数据状态含义(需要解析0或1的含义给前端)
*/
private String statusName;
/**
* 数据集合
*/
private List<BusinessEntity> list;
}

二:设计思路

​ 就像@JsonFormat注解,可以指定返回日期格式。我们是不是可以也自定义一个注解,通过这个注解,我们可以自动的把需要联表的数据userName自动填充,需要解析的数据数据statusName如何通过枚举解析。

​ 故定义枚举@AutowiredAttribute如下

/**
* 自动装配属性
*/
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Documented
public @interface AutowiredAttribute { /**
* 当为默认值时,表明该属性为javaBean,且该javaBean需要自动注入属性
* 否则为指向的某一个属性
*
* @return
*/
String param() default ""; /**
* 默认为BaseEnum.class,
* 当为默认时注入数据的来源时redis缓存,
* 否则为枚举类型
*
* @return
*/
Class<? extends BaseEnum> enumClass() default BaseEnum.class; /**
* 数据源
*
* @return
*/
DataSourceEnum dataSource() default DataSourceEnum.EMPTY; }

定义公共枚举继承继承接口BaseEnum

public interface BaseEnum {

    String getCode();

    String getMsg();
}

定义数据源枚举如下dataSource

public enum DataSourceEnum implements BaseEnum {

    SYSTEM_DICT("sys:dict:", "系统字典值", "sys_dict_value", "name"),
USER_NAME("user:name:", "用户的id与姓名的映射", "sys_user", "user_name"),
USER_ROLE("user:role:", "角色id于角色名称映射", "sys_role", "name"),
DEPT_NAME("dept:name:", "部门的id与部门名称的映射", "sys_dept", "name"),
EMPTY("00", "默认", "", ""); DataSourceEnum(String code, String msg, String tableName, String tableColumn) {
this.code = code;
this.msg = msg;
this.tableName = tableName;
this.tableColumn = tableColumn;
} private String code;
private String msg; /**
* 表明
*/
private String tableName;
/**
* 表的列
*/
private String tableColumn; @Override
public String getCode() {
return code;
} @Override
public String getMsg() {
return msg;
} public String getTableName() {
return tableName;
} public String getTableColumn() {
return tableColumn;
}
}

三:使用方法

对比原对象:通过新增注解,就避免的关联查询和数据解析

public class BusinessEntity {

    /**
* 创建者id
*/
private Long createUserId;
/**
* 创建者名称 (需要关联用户表)
*/
@AutowiredAttribute(param = "createUserId", dataSource = DataSourceEnum.USER_NAME)
private String userName; /**
* 数据状态(0:有效,1失效)
*/
private String status; /**
* 数据状态含义(需要解析0或1的含义给前端)
*/
@AutowiredAttribute(param = "status", enumClass = StatusEnum.class)
private String statusName;
/**
* 数据集合
*/
@AutowiredAttribute
private List<BusinessEntity> list;
}

四:注解解析器(核心代码)

/**
* 填充相应体
*/
@Component
@ControllerAdvice()
public class FillResponseBodyAdvice implements ResponseBodyAdvice { @Autowired
RedissonClient redissonClient; @Autowired
JdbcTemplate jdbcTemplate; private static String GET_CODE_METHOD_NAME = "getCode";
private static String GET_MSG_METHOD_NAME = "getMsg"; @Override
public boolean supports(MethodParameter returnType, Class converterType) {
if (ResponseResult.class.getName().equals(returnType.getMethod().getReturnType().getName())) {
return true;
}
return false;
} @Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (((ResponseResult<?>) body).getCode() == 200) {//仅仅对相应为200结果处理
Object data = ((ResponseResult<?>) body).getData();
Class<?> aClass = data.getClass();
if (data instanceof List) {
//集合对象设置属性
setForListBeanArr((List) data);
} else {
//判断是否为自定义java对象
if (aClass.getSuperclass() instanceof Object) {
setForJavaBeanArr(data, aClass);
}
}
}
return body;
} /**
* 为集合对象设置属性
*
* @param list
*/
void setForListBeanArr(List<Object> list) {
for (Object object : list) {
Class<?> aClass = object.getClass();
setForJavaBeanArr(object, aClass);
}
} /**
* 为自定义javaBean对象设置值
*/
private void setForJavaBeanArr(Object data, Class<?> aClass) {
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field : declaredFields) {
AutowiredAttribute annotation = field.getAnnotation(AutowiredAttribute.class);
if (annotation == null) {
continue;
}
//通过枚举注入
String param = annotation.param();
try {
field.setAccessible(true);
if (param.equals("")) {//注解表明该对象时javaBean对象
//获取该javaBean对象
Object data2 = field.get(data);
if (data2 == null) {
continue;
}
//属性是list对象
if (data2 instanceof List) {
setForListBeanArr((List) data2);
} else if (data2.getClass().getSuperclass() instanceof Object) {
setForJavaBeanArr(data2, data2.getClass());
} } else {
//反射获取值
Field field1 = aClass.getDeclaredField(param);
field1.setAccessible(true);
Object o = field1.get(data);
if (annotation.enumClass().getName().equals(BaseEnum.class.getName())) {
//通过redis注入
injectByEnum(o, field, data);
} else {
//通过枚举注入
injectByRedis(o, field, data);
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
} private void injectByEnum(Object param, Field field, Object data) throws IllegalAccessException {
AutowiredAttribute annotationAutowiredAttribute = field.getAnnotation(AutowiredAttribute.class);
DataSourceEnum dataSourceEnum = annotationAutowiredAttribute.dataSource();
if (dataSourceEnum.equals(DataSourceEnum.EMPTY)) {
//不规范的
} else if (dataSourceEnum.equals(DataSourceEnum.SYSTEM_DICT)) {
Object o = redissonClient.getMap(DataSourceEnum.SYSTEM_DICT.getCode()).get(param);
if (o == null) {
o = getDictAndSetRedis(DataSourceEnum.SYSTEM_DICT, param);
}
field.set(data, o);
}
} private void injectByRedis(Object param, Field field, Object data) throws IllegalAccessException {
AutowiredAttribute annotation = field.getAnnotation(AutowiredAttribute.class);
Class<? extends BaseEnum> aClass = annotation.enumClass();
try {
// 获取所有常量
Object[] objects = aClass.getEnumConstants();
//获取指定方法
Method getMsg = aClass.getMethod(GET_MSG_METHOD_NAME);
//获取指定方法
Method getCode = aClass.getMethod(GET_CODE_METHOD_NAME);
for (Object obj : objects) {
if (getCode.invoke(obj).equals(param.toString())) {
field.set(data, getMsg.invoke(obj));
System.out.println(getMsg.invoke(obj));
}
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
} //
Object getDictAndSetRedis(DataSourceEnum dataSourceEnum, Object value) {
String sql = "select name from " + dataSourceEnum.getTableName() + " where id = " + value;
String s = jdbcTemplate.queryForObject(sql, String.class);
RMap<Object, Object> map = redissonClient.getMap(dataSourceEnum.getCode());
map.put(value, s);
return s;
}
}

实现了从数据库(mysql)自动查询,并把结果缓冲到数据库。

五:需要思考的技术点

1.为什么注解要用到枚举和泛型class

2.注解解析器,为什么用ResponseBodyAdvice这里解析?不在Filter,Interceptors?

3.对于对象里面嵌套对象,或对象里面嵌套集合,怎么解决注入?递归

结束语

欢迎大家使用,如果遇到问题可一起探讨,如果帮助了你可点赞子支持一下。

基于SpringBoot实现自动装配返回属性的更多相关文章

  1. 深入理解SpringBoot之自动装配

    SpringBoot的自动装配是拆箱即用的基础,也是微服务化的前提.其实它并不那么神秘,我在这之前已经写过最基本的实现了,大家可以参考这篇文章.这次主要的议题是,来看看它是怎么样实现的,我们透过源代码 ...

  2. Spring入门(5)-自动装配Bean属性

    Spring入门(5)-自动装配Bean属性 本文介绍如何装配Bean属性. 0. 目录 ByName ByType constructor 默认自动装配 混合使用自动装配和显示装配 1. ByNam ...

  3. 【springboot】自动装配原理

    摘自:https://mp.weixin.qq.com/s/ZxY_AiJ1m3z1kH6juh2XHw 前言 Spring翻译为中文是"春天",的确,在某段时间内,它给Java开 ...

  4. Spring学习笔记--自动装配Bean属性

    Spring提供了四种类型的自动装配策略: byName – 把与Bean的属性具有相同名字(或者ID)的其他Bean自动装配到Bean的对应属性中. byType – 把与Bean的属性具有相同类型 ...

  5. Spring基于的注解自动装配和依赖注入(***)

    #自动装配的小Demo: package com.gyf.annotation; //DAO层 public interface UserDao { public void save(); } pac ...

  6. Spring课程 Spring入门篇 4-8 Spring bean装配之基于java的容器注解说明--基于泛型的自动装配

    1 解析 1.1 什么是泛型? 1.2 泛型有什么作用? 1.3 泛型装配样式? 2 代码演练 2.1 泛型应用 1 解析 1.1 什么是泛型? Java泛型设计原则:只要在编译时期没有出现警告,那么 ...

  7. SpringBoot配置文件自动映射到属性和实体类(8)

    一.配置文件加载 1.Controller中配置并指向文件 @Controller @PropertySource(value = { "application.properties&quo ...

  8. Spring学习(16)--- 基于Java类的配置Bean 之 基于泛型的自动装配(spring4新增)

    例子: 定义泛型Store package javabased; public interface Store<T> { } 两个实现类StringStore,IntegerStore p ...

  9. 基于java容器注解---基于泛型的自动装配

    上面利用@Configuration和@Bean配置bean,中间利用@Autowired,指定s1和s2分别指向哪个实现类,下面利用@Autowired,指定s中只有Integer实现类 例子: 在 ...

随机推荐

  1. 前端框架 bootstrap 的使用

    内容概要 前端框架 bootstrap 简介 引入方式 布局容器 栅格系统 图标 内容详细 简介 网址:https://v3.bootcss.com/ 版本: 2.X 3.X 4.X 推荐使用3.X版 ...

  2. Spring中的Environment外部化配置管理详解

    Environment的中文意思是环境,它表示整个spring应用运行时的环境信息,它包含两个关键因素 profiles properties profiles profiles这个概念相信大家都已经 ...

  3. 【Kotlin】初识Kotlin(二)

    [Kotlin]初识Kotlin(二) 1.Kotlin的流程控制 流程控制是一门语言中最重要的部分之一,从最经典的if...else...,到之后的switch,再到循环控制的for循环和while ...

  4. windows mysql数据存储路径更改

    背景:之前服务器磁盘很小,随着数据量的不断增加,磁盘不够,所以新申请了更大的磁盘,然后需要将旧路径下的数据迁移到新磁盘上. 1.任务管理器-打开服务,找到mysql的启动项,停止服务,属性查看可执行文 ...

  5. NSSCTF-gif好像有点大

    下载的附件压缩包是一个接近9mb的压缩包,其实是比较简单的一个小杂项,应该就算是个签到,接下来就是使用工具进行解答,可以使用LSB隐写工具,一帧一帧的查看,最后是在500多看到的一个是存在二维码的,扫 ...

  6. [题解]Mail.Ru Cup 2018 Round 1 - C. Candies Distribution

    [题目] C. Candies Distribution [描述] n个小朋友排排坐吃糖糖,小朋友从左到右编号1到n.每个小朋友手上有一定数量的糖.对于第i个小朋友来说,编号比他小的小朋友中有li个小 ...

  7. 自助式bi工具为什么这么受欢迎?

    ​目前比较流行的一种BI形式,当属于自助式BI分析,也就是自助分析平台,即在这个倡导凡事自助的社会中,BI也要以这种形式来呈现.自助式的BI分析相比较于传统的形式,是有很多优点的,我为大家整理了一版. ...

  8. 深度学习分类问题中accuracy等评价指标的理解

    在处理深度学习分类问题时,会用到一些评价指标,如accuracy(准确率)等.刚开始接触时会感觉有点多有点绕,不太好理解.本文写出我的理解,同时以语音唤醒(唤醒词识别)来举例,希望能加深理解这些指标. ...

  9. 【C# 异常处理】 开端

    异常概述 在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美,在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避免的,比如:客户输入数据的格式,读取文件是否存在,网络是 ...

  10. Python变量与常见数据类型

    Python变量 变量与常量:有时被通称为变量 变量:通常指代能够产生变化的量,多用以描述事物的变化状态 常量:通常指代稳定不产生变化的量,多用于描述事物的固定状态 # 代码层面的使用 myname ...