基于SpringBoot实现自动装配返回属性的设计思路
作者:爱我-中华
这篇文章主要介绍了基于SpringBoot实现自动装配返回属性,这里涉及到的技术知识点有注解解析器,为什么用ResponseBodyAdvice这里解析?不在Filter,Interceptors,本文结合示例代码给大家介绍的非常详细,需要的朋友参考下吧
一:需求背景
在业务开发中经常会有这个一个场景,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缓存, * 否则为枚举类型 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<BusinessEntity> 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<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) { 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实现自动装配返回属性的文章就介绍到这了,更多相关SpringBoot自动装配返回属性内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!