立即前往

活动

天翼云最新优惠活动,涵盖免费试用,产品折扣等,助您降本增效!
查看全部活动
热门活动
  • 智算采购季 热销S6云服务器2核4G限时88元/年起,部分主机可加赠对象存储组合包!
  • 免费体验DeepSeek,上天翼云息壤 NEW 新老用户均可免费体验2500万Tokens,限时两周
  • 云上钜惠 HOT 爆款云主机全场特惠,更有万元锦鲤券等你来领!
  • 算力套餐 HOT 让算力触手可及
  • 天翼云脑AOne NEW 连接、保护、办公,All-in-One!
  • 一键部署Llama3大模型学习机 0代码一键部署,预装最新主流大模型Llama3与StableDiffusion
  • 中小企业应用上云专场 产品组合下单即享折上9折起,助力企业快速上云
  • 息壤高校钜惠活动 NEW 天翼云息壤杯高校AI大赛,数款产品享受线上订购超值特惠
  • 天翼云电脑专场 HOT 移动办公新选择,爆款4核8G畅享1年3.5折起,快来抢购!
  • 天翼云奖励推广计划 加入成为云推官,推荐新用户注册下单得现金奖励
免费活动
  • 免费试用中心 HOT 多款云产品免费试用,快来开启云上之旅
  • 天翼云用户体验官 NEW 您的洞察,重塑科技边界

智算服务

打造统一的产品能力,实现算网调度、训练推理、技术架构、资源管理一体化智算服务
智算云(DeepSeek专区)
科研助手
  • 算力商城
  • 应用商城
  • 开发机
  • 并行计算
算力互联调度平台
  • 应用市场
  • 算力市场
  • 算力调度推荐
一站式智算服务平台
  • 模型广场
  • 体验中心
  • 服务接入
智算一体机
  • 智算一体机
大模型
  • DeepSeek-R1-昇腾版(671B)
  • DeepSeek-R1-英伟达版(671B)
  • DeepSeek-V3-昇腾版(671B)
  • DeepSeek-R1-Distill-Llama-70B
  • DeepSeek-R1-Distill-Qwen-32B
  • Qwen2-72B-Instruct
  • StableDiffusion-V2.1
  • TeleChat-12B

应用商城

天翼云精选行业优秀合作伙伴及千余款商品,提供一站式云上应用服务
进入甄选商城进入云市场创新解决方案
办公协同
  • WPS云文档
  • 安全邮箱
  • EMM手机管家
  • 智能商业平台
财务管理
  • 工资条
  • 税务风控云
企业应用
  • 翼信息化运维服务
  • 翼视频云归档解决方案
工业能源
  • 智慧工厂_生产流程管理解决方案
  • 智慧工地
建站工具
  • SSL证书
  • 新域名服务
网络工具
  • 翼云加速
灾备迁移
  • 云管家2.0
  • 翼备份
资源管理
  • 全栈混合云敏捷版(软件)
  • 全栈混合云敏捷版(一体机)
行业应用
  • 翼电子教室
  • 翼智慧显示一体化解决方案

合作伙伴

天翼云携手合作伙伴,共创云上生态,合作共赢
天翼云生态合作中心
  • 天翼云生态合作中心
天翼云渠道合作伙伴
  • 天翼云代理渠道合作伙伴
天翼云服务合作伙伴
  • 天翼云集成商交付能力认证
天翼云应用合作伙伴
  • 天翼云云市场合作伙伴
  • 天翼云甄选商城合作伙伴
天翼云技术合作伙伴
  • 天翼云OpenAPI中心
  • 天翼云EasyCoding平台
天翼云培训认证
  • 天翼云学堂
  • 天翼云市场商学院
天翼云合作计划
  • 云汇计划
天翼云东升计划
  • 适配中心
  • 东升计划
  • 适配互认证

开发者

开发者相关功能入口汇聚
技术社区
  • 专栏文章
  • 互动问答
  • 技术视频
资源与工具
  • OpenAPI中心
开放能力
  • EasyCoding敏捷开发平台
培训与认证
  • 天翼云学堂
  • 天翼云认证
魔乐社区
  • 魔乐社区

支持与服务

为您提供全方位支持与服务,全流程技术保障,助您轻松上云,安全无忧
文档与工具
  • 文档中心
  • 新手上云
  • 自助服务
  • OpenAPI中心
定价
  • 价格计算器
  • 定价策略
基础服务
  • 售前咨询
  • 在线支持
  • 在线支持
  • 工单服务
  • 建议与反馈
  • 用户体验官
  • 服务保障
  • 客户公告
  • 会员中心
增值服务
  • 红心服务
  • 客户支持计划
  • 专家技术服务
  • 备案管家

了解天翼云

天翼云秉承央企使命,致力于成为数字经济主力军,投身科技强国伟大事业,为用户提供安全、普惠云服务
品牌介绍
  • 关于天翼云
  • 智算云
  • 天翼云4.0
  • 新闻资讯
  • 天翼云APP
基础设施
  • 全球基础设施
  • 产品能力
  • 信任中心
最佳实践
  • 精选案例
  • 超级探访
  • 云杂志
  • 分析师和白皮书
  • 天翼云·创新直播间
市场活动
  • 2025智能云生态大会
  • 2024智算云生态大会
  • 2023云生态大会
  • 2022云生态大会
  • 天翼云中国行
天翼云
  • 活动
  • 智算服务
  • 产品
  • 解决方案
  • 应用商城
  • 合作伙伴
  • 开发者
  • 支持与服务
  • 了解天翼云
    • 关系数据库SQL Server版
    • 企业主机安全
    • 云防火墙
    • CDN加速
    • 物理机
    • GPU云主机
    • 天翼云电脑(政企版)
    • 天翼云电脑(公众版)
    • 云主机备份
    • 弹性云主机
      搜索发现
      关系数据库SQL Server版企业主机安全云防火墙CDN加速物理机GPU云主机天翼云电脑(政企版)天翼云电脑(公众版)云主机备份弹性云主机
    • 文档
    • 控制中心
    • 备案
    • 管理中心
    • 登录
    • 免费注册

    基于SpringBoot实现自动装配返回属性

    首页 知识中心 软件开发 文章详情页

    基于SpringBoot实现自动装配返回属性

    2024-09-25 10:14:09 阅读次数:377

    mysql,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缓存,
         * 否则为枚举类型
         *
         * @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 {
        private static final Logger log = LoggerFactory.getLogger(FillResponseBodyAdvice.class);
        @Autowired
        RedissonClient redissonClient;
    
        @Autowired
        JdbcTemplate jdbcTemplate;
    
        private static String GET_CODE_METHOD_NAME = "getCode";
        private static String GET_MSG_METHOD_NAME = "getMsg";
        String pageName = Page.class.getName();//分页对象名
        String baseEntityName = BaseEntity.class.getName();//自定义基础对象
    
        @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) {
            try {
                if (((ResponseResult<?>) body).getCode() == 200 && Objects.nonNull(body)) {//仅仅对相应为200结果处理
                    Object data = ((ResponseResult<?>) body).getData();
                    Class<?> aClass = data.getClass();
                    //根路径从
                    String valueClassName = data.getClass().getName();
                    if (valueClassName.equals(pageName)) {
                        //分页对象
                        List records = ((Page<?>) data).getRecords();
                        setForListBeanArr(records);
                    } else if (data instanceof List) {
                        //集合对象设置属性
                        setForListBeanArr((List) data);
                    } else {
                        //判断是否为自定义java对象
                        if (aClass.getSuperclass() instanceof Object || valueClassName.equals(baseEntityName)) {
                            setForJavaBeanArr(data, aClass);
                        }
                    }
                }
            } catch (Exception e) {
                ("autowired attribute fail msg = {}", e.getMessage());
            }
            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 || data2.getClass().getName().equals(baseEntityName)) {
                            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 {
                Object bucketValue = redissonClient.getBucket(dataSourceEnum.getCode() + param).get();
                if (Objects.isNull(bucketValue)) {
                    bucketValue = getDictAndSetRedis(dataSourceEnum, param);
                }
                field.set(data, bucketValue);
            }
        }
    
        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 = String.format("select %s from %s where id = %s", dataSourceEnum.getTableColumn(), dataSourceEnum.getTableName(), value);
            ("autowired attribute from sql = {}", sql);
            String dataValue = jdbcTemplate.queryForObject(sql, String.class);
            RBucket<Object> bucket = redissonClient.getBucket(dataSourceEnum.getCode() + value);
            bucket.set(dataValue);
            bucket.expire(30, TimeUnit.MINUTES);
            return dataValue;
        }
    }
    

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

    五:需要思考的技术点

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

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

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

    版权声明:本文内容来自第三方投稿或授权转载,原文地址:https://www.cnblogs.com/jinliang374003909/p/16051843.html,作者:爱我-中华,版权归原作者所有。本网站转在其作品的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如因作品内容、版权等问题需要同本网站联系,请发邮件至ctyunbbs@chinatelecom.cn沟通。

    上一篇:Linux:ansible-playbook配置文件(剧本)(进阶)

    下一篇:使用java底层实现邮件的发送(含测试,源码)

    相关文章

    2025-04-22 09:27:28

    SpringBoot 自定义TaskExecutor线程池执行异步操作

    SpringBoot 自定义TaskExecutor线程池执行异步操作

    2025-04-22 09:27:28
    SpringBoot , 异步 , 线程 , 自定义
    2025-04-11 07:15:54

    java使用JDBC方式操作mysql数据库示例

    java使用JDBC方式操作mysql数据库示例

    2025-04-11 07:15:54
    java , JDBC , mysql , 数据库 , 示例
    2025-04-11 07:12:29

    基于主流SpringBoot进行JavaWeb开发的学习路线

    基于主流SpringBoot进行JavaWeb开发的学习路线

    2025-04-11 07:12:29
    Java , SpringBoot , Vue , Web , 学习 , 开发 , 接口
    2025-04-09 09:17:07

    mysql Commands out of sync; you can‘t run this command now

    mysql Commands out of sync; you can‘t run this command now

    2025-04-09 09:17:07
    mysql , query , 执行
    2025-03-31 08:57:16

    PDO ping 的实例 ,解决mysql has gone的问题

    PDO ping 的实例 ,解决mysql has gone的问题

    2025-03-31 08:57:16
    mysql
    2025-03-27 09:41:58

    写在前面【mysql高级】【java提高】

    写在前面【mysql高级】【java提高】

    2025-03-27 09:41:58
    mysql , MySQL , 备份 , 数据库 , 日志 , 索引
    2025-03-26 09:08:50

    navicat导入excel文件的步骤以及可能碰到的问题

    navicat导入excel文件的步骤以及可能碰到的问题

    2025-03-26 09:08:50
    excel , mysql , navicat , 导入 , 数据
    2025-03-24 08:52:40

    解决 MySQL 服务无法启动:failed to restart mysql.service: unit not found

    解决 MySQL 服务无法启动:failed to restart mysql.service: unit not found

    2025-03-24 08:52:40
    mysql , MySQL , service , 文件 , 服务
    2025-03-18 08:27:03

    简单按日期查询mysql某张表中的记录数

    简单按日期查询mysql某张表中的记录数

    2025-03-18 08:27:03
    count , mysql
    2025-03-17 08:48:47

    【判断数据库在线】shell 判断数据库在线

    【判断数据库在线】shell 判断数据库在线

    2025-03-17 08:48:47
    client , localhost , mysql , MySQL , root
    查看更多
    推荐标签

    作者介绍

    天翼云小翼
    天翼云用户

    文章

    32777

    阅读量

    4833314

    查看更多

    最新文章

    SpringBoot 自定义TaskExecutor线程池执行异步操作

    2025-04-22 09:27:28

    基于主流SpringBoot进行JavaWeb开发的学习路线

    2025-04-11 07:12:29

    【源码改造】flink JDBC connector 源码改造之 类型转换 java.time.LocalDateTime cannot be cast to java.sql.Timestamp

    2025-03-17 07:50:16

    杀掉链接mysql库线程的方法

    2025-03-17 07:50:08

    升级SpringBoot集成sharding-jdbc报shardingsphere.shardingjdbc.jdbc.unsupported.AbstractUnsupportedOperatio

    2025-03-12 09:31:27

    毕设--基于 SpringBoot 垃圾分类网站设计与实现

    2025-03-10 09:52:02

    查看更多

    热门文章

    MySQL 5.7 JSON函数学习

    2023-04-27 08:00:00

    mysql列存储引擎-字符串属性列-列压缩测试

    2023-04-23 09:34:23

    SpringBoot 单元测试(三十六)

    2024-09-25 10:14:09

    Ajax提交数据SpringBoot后台报错“HttpMessageNotReadableException: JSON parse error: Cannot construct instance“

    2023-06-13 08:29:06

    AOP 中 获取常见的各种参数方法合集

    2023-05-15 10:01:00

    Python编程:利用上下文管理器管理MySQL的链接对象

    2023-02-21 03:02:11

    查看更多

    热门标签

    java Java python 编程开发 开发语言 代码 算法 线程 html Python 数组 C++ javascript c++ 元素
    查看更多

    相关产品

    弹性云主机

    随时自助获取、弹性伸缩的云服务器资源

    天翼云电脑(公众版)

    便捷、安全、高效的云电脑服务

    对象存储

    高品质、低成本的云上存储服务

    云硬盘

    为云上计算资源提供持久性块存储

    查看更多

    随机文章

    请简单介绍Spring支持的常用数据库事务传播属性和事务隔离级别

    基于SpringBoot+Vue的餐饮管理系统设计与实现

    SpringBoot系列---【StopWatch优雅实现统计代码耗时】

    SpringBoot学习笔记-13:第十三章-SpringBoot 与安全

    小试牛刀 docker三个mysql走起

    基于django的公寓住宿管理python网站开发问题记录1

    • 7*24小时售后
    • 无忧退款
    • 免费备案
    • 专家服务
    售前咨询热线
    400-810-9889转1
    关注天翼云
    • 权益商城
    • 天翼云APP
    • 天翼云微信公众号
    服务与支持
    • 备案中心
    • 售前咨询
    • 智能客服
    • 自助服务
    • 工单管理
    • 客户公告
    • 涉诈举报
    账户管理
    • 管理中心
    • 订单管理
    • 余额管理
    • 发票管理
    • 充值汇款
    • 续费管理
    快速入口
    • 权益商城
    • 文档中心
    • 最新活动
    • 免费试用
    • 信任中心
    • 天翼云学堂
    云网生态
    • 甄选商城
    • 渠道合作
    • 云市场合作
    了解天翼云
    • 关于天翼云
    • 天翼云APP
    • 服务案例
    • 新闻资讯
    • 联系我们
    热门产品
    • 云电脑
    • 弹性云主机
    • 云电脑政企版
    • 天翼云手机
    • 云数据库
    • 对象存储
    • 云硬盘
    • Web应用防火墙
    • 服务器安全卫士
    • CDN加速
    热门推荐
    • 云服务备份
    • 边缘安全加速平台
    • 全站加速
    • 安全加速
    • 云服务器
    • 云主机
    • 智能边缘云
    • 应用编排服务
    • 微服务引擎
    • 共享流量包
    更多推荐
    • web应用防火墙
    • 密钥管理
    • 等保咨询
    • 安全专区
    • 应用运维管理
    • 云日志服务
    • 文档数据库服务
    • 云搜索服务
    • 数据湖探索
    • 数据仓库服务
    友情链接
    • 中国电信集团
    • 189邮箱
    • 天翼企业云盘
    • 天翼云盘
    ©2025 天翼云科技有限公司版权所有 增值电信业务经营许可证A2.B1.B2-20090001
    公司地址:北京市东城区青龙胡同甲1号、3号2幢2层205-32室
    • 用户协议
    • 隐私政策
    • 个人信息保护
    • 法律声明
    备案 京公网安备11010802043424号 京ICP备 2021034386号