基于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<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自动装配返回属性内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅析SpringBoot自动装配的实现

    目录 背景 解析 起始 具体解析 结论 备注 背景 众所周知,如下即可启动一个最简单的Spring应用.查看@SpringBootApplication注解的源码,发现这个注解上有一个重要的注解@EnableAutoConfiguration,而这个注解就是SpringBoot实现自动装配的基础 import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.Spri

  • 深入了解Java SpringBoot自动装配原理

    目录 自动装配原理 SpringBootApplication EnableAutoConfiguration AutoConfigurationImportSelector 总结 在使用springboot时,很多配置我们都没有做,都是springboot在帮我们完成,这很大一部分归功于springboot自动装配,那springboot的自动装配的原理是怎么实现的呢? 自动装配原理 springboot 版本:2.4.3 SpringBootApplication springboot启动类

  • SpringBoot2自动装配原理解析

    目录 1.SpringBoot特点 1.1依赖管理 1.2自动配置 2.容器功能 2.1组件添加 1.@Configuration 2.@Bean.@Component.@Controller.@Service.@Repository 3.@ComponentScan.@Import 4.@Conditional 2.2.原生配置引入@ImportResource 2.3配置绑定 1.@ConfigurationProperties 3.自动配置原理入门 3.1引导加载自动配置类 3.2按需开启

  • springboot 无法自动装配的问题

    目录 springboot 无法自动装配 @Autowired 报错:无法自动装配 基本上是因为 放到org.example下,问题解决 原因 无法自动装配.未找到“xxxMapper”类型的bean 说明Spring框架没有识别到你的xxxMapper中的类 如果你得类不需要管理或者继承或实现一些规则 springboot 无法自动装配 @Autowired 报错:无法自动装配 基本上是因为 1.项目里有类似mybatis @Mapper这种第三方映射类,需要用到springboot auto

  • 聊聊SpringBoot自动装配的魔力

    目录 一. springBoot自动配置的好处 1. 回想一下当你在使用spring来搭建一个项目的时候 2.这时我们会有一个疑问 二.@Conditional注解相关介绍 1.@Conditional小demo 三.自定义一个条件配置类/springBoot自定义注解 1.简单解释@Condition注解家族 2.自定义条件注解 一. springBoot自动配置的好处 1. 回想一下当你在使用spring来搭建一个项目的时候 你需要编写很多的有关spring的xml.例如读取属性配置的bea

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

    目录 一:需求背景 二:设计思路 三:使用方法 四:注解解析器(核心代码) 五:需要思考的技术点 一:需求背景 在业务开发中经常会有这个一个场景,A(业务表)表中会记录数据的创建人,通常我们会用userId字段记录该数据的创建者,但数据的使用方会要求展示该数据的创建者姓名,故我们会关联用户表拿该用户的姓名.还有一些枚举值的含义也要展示给前端.导致原本一个单表的sql就要写成多表的关联sql,以及枚举含义的转换很是恶心. 例如:业务对象BusinessEntity.java public clas

  • 基于Java反射的map自动装配JavaBean工具类设计示例代码

    前言 JavaBean是一个特殊的java类,本文将给大家详细介绍关于基于Java反射的map自动装配JavaBean工具类设计的相关内容,下面话不多说了,来一起看看详细的介绍吧 方法如下 我们平时在用Myabtis时不是常常需要用map来传递参数,大体是如下的步骤: public List<Role> findRoles(Map<String,Object> param); <select id="dindRoles" parameterType=&qu

  • 基于Springboot商品进销存管理系统的设计与实现

    目录 一.项目简介 二.环境介绍 三.系统展示 四.核心代码展示 五.项目总结 一.项目简介 本项目实现了基于springboot的进销存管理系统,主要用户开设网店的相关商品的进货.销售.库存的管理,功能比较完整,有着完备的权限管理系统,可以自行根据需要来设计角色和分配权限,权限的粒度可以做到页面级的权限控制,整个项目来讲比较优秀.主要实现的功能有如下几个模块: 基础管理模块:包含客户管理.供应商管理.商品管理三个子模块 进货管理模块:包含商品进货.退货.商品退货查询几个子查块 销售管理:包含商

  • SpringBoot(cloud)自动装配bean找不到类型的问题

    目录 SpringBoot自动装配bean找不到类型 今天我就犯了因为boot扫不到包的问题 看项目结构 很明显 无法自动装配.未找到“xxxMapper”类型的bean SpringBoot自动装配bean找不到类型 Spring基于注解的@Autowired是比较常用的自动装配注解,但是会因为个人的疏忽,SSM进行配置的时候没有将对应bean所在包给扫描进去:或者使用Boot的时候,没有放在启动类所在包及其子包下导致报错. 今天我就犯了因为boot扫不到包的问题 Description: F

  • SpringBoot自动装配原理详解

    首先对于一个SpringBoot工程来说,最明显的标志的就是 @SpringBootApplication它标记了这是一个SpringBoot工程,所以今天的 SpringBoot自动装配原理也就是从它开始说起. 自动装配流程 首先我们来看下@SpringBootApplication 这个注解的背后又有什么玄机呢,我们按下 ctrl + 鼠标左键,轻轻的点一下,此时见证奇迹的时刻.. 我们看到如下优雅的代码: 这其中有两个比较容易引起我们注意的地方,一个是@SpringBootConfigur

  • SpringBoot 自动装配的原理详解分析

    目录 前言 自动装配案例 自动装配分析 自动装配总结 前言 关于 ​​SpringBoot​​​ 的自动装配功能,相信是每一个 ​​Java​​ 程序员天天都会用到的一个功能,但是它究竟是如何实现的呢?今天阿粉来带大家看一下. 自动装配案例 首先我们通过一个案例来看一下自动装配的效果,创建一个 ​​SpringBoot​​ 的项目,在 ​​pom​​ 文件中加入下面的依赖. <dependency> <groupId>org.springframework.boot</gro

  • 深入浅析SpringBoot中的自动装配

    SpringBoot的自动装配是拆箱即用的基础,也是微服务化的前提.这次主要的议题是,来看看它是怎么样实现的,我们透过源代码来把握自动装配的来龙去脉. 一.自动装配过程分析 1.1.关于@SpringBootApplication 我们在编写SpringBoot项目时,@SpringBootApplication是最常见的注解了,我们可以看一下源代码: /* * Copyright 2012-2017 the original author or authors. * * Licensed un

随机推荐