基于SpringBoot实现自动装配返回属性的设计思路

网友投稿 272 2022-08-17


基于SpringBoot实现自动装配返回属性的设计思路

目录一:需求背景二:设计思路三:使用方法四:注解解析器(核心代码)五:需要思考的技术点

一:需求背景

在业务开发中经常会有这个一个场景,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 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缓存,

* 否则为枚举类型

Class extends BaseEnum> enumClass() default BaseEnum.class;

* 数据源

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;

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 list;

}

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

/**

* 填充相应体

*/

@Component

@ControllerAdvice()

public class FillResponseBodyAdvice implements ResponseBodyAdvice {

@Autowired

RedissonClient redissonClient;

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;

}

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 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(datahttp://2, 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) {

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 map = redissonClient.getMap(dataSourceEnum.getCode());

map.put(value, s);

return s;

}

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

五:需要思考的技术点

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

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

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


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:基于java语言实现快递系统
下一篇:netty服务端辅助类ServerBootstrap创建逻辑分析
相关文章

 发表评论

暂时没有评论,来抢沙发吧~