自定义注解加反射实现数据类型转换

概述

通过ORM框架从数据库中查询出的实体对象,大部分情况下可能与前端页面展示的数据结果类型略有不同,比如:后端定义的店铺实体类(Shop)中有店铺状态:0-正常,1-未审核,2-违规,3-倒闭,在做脱敏的处理下需要转换成一个VO,其中店铺状态需要后台转换,因此可以通过自定义注解+反射的方式处理。

准备工作:

1、自定义注解

1.1 ConvertEnumField.java

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ConvertEnumField {

    /** 枚举类.class */
    Class<?> enumClass();

    /** 实体类的字段名 */
    String sourceFieldName() default "";

}

1.2 ConvertDateField.java

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ConvertDateField {

    /** 实体类的字段名 */
    String sourceFieldName() default "";

    /**
     * 日期的格式,例如:
     *
     * yyyy-MM-dd
     *
     * yyyy-MM-dd HH:mm:ss
     *
     * @return
     */
    String pattern() default "";

}

2、枚举:ShopStatusEnum.java

public enum ShopStatusEnum {

    /** 铺状态:0-正常,1-未审核,2-违规,3-倒闭 */

    NORMAL(0, "正常"),

    UNAUDITED(1, "未审核"),

    GET_OUT_OF_LINE(2, "违规"),

    CLOSE_DOWN(3, "倒闭");

    /** 状态码 **/
    private Integer code ;

    /** 消息 **/
    private String message ;

    ShopStatusEnum(Integer code, String message) {
        this.code = code ;
        this.message = message ;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

3、Shop.java (实体)

@Data
public class Shop implements Serializable {

    /**
     * 主键
     */
    private Long id = 22222L;

    /**
     * 店铺名称
     */
    private String shopName = "test";

    /**
     * 店铺状态:0-正常,1-未审核,2-违规,3-倒闭
     */
    private Integer shopStatus = 1;

    /**
     * 创建时间
     */
    private LocalDateTime createTime = LocalDateTime.now();

    /**
     * 修改时间
     */
    private Date updateTime;

    /**
     * 是否删除:0-正常,1-删除
     */
    private Integer isDeleted;

    private static final long serialVersionUID = 1L;
}

4、ShopVo.java(前端展示的实体)

@Data
public class ShopVo {

    /**
     * 主键
     */
    private Long id;

    /**
     * 店铺名称
     */
    private String shopName;

    /**
     * 店铺状态:0-正常,1-未审核,2-违规,3-倒闭
     */
    @ConvertEnumField(enumClass = ShopStatusEnum.class, sourceFieldName = "shopStatus")
    private String shopStatusStr;

    /**
     * 创建时间
     */
    @ConvertDateField(sourceFieldName = "createTime", pattern = "yyyy-MM-dd")
    private String createTimeStr;

    /**
     * 修改时间
     */
    private Date updateTime;

    /**
     * 是否删除:0-正常,1-删除
     */
    private Integer isDeleted;

    private static final long serialVersionUID = 1L;
}

自定义工具类:BeanUtil.java

import com.javasetest.annotation.ConvertDateField;
import com.javasetest.annotation.ConvertEnumField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;

/**
 * ClassName: BeanUtil
 * Package: com.javasetest.utils
 * Description:
 *
 * @Author wxz
 * @Create 2024/4/12 16:08
 * @Version 1.0
 */
public class BeanUtil {

    private static final Logger logger = LoggerFactory.getLogger(BeanUtil.class);

    /**
     * 字段转换
     *
     * @param sourceObj 源数据
     * @param targetObj 目标数据
     */
    public static void fieldConvert(Object sourceObj, Object targetObj) {

        // 获取目标数据的所有字段
        Field[] targetFields = targetObj.getClass().getDeclaredFields();

        // 遍历所有字段是否标注了注解
        Arrays.stream(targetFields).forEach(field -> {
            // 由于这是私有字段,我们需要设置为可访问
            field.setAccessible(true);

            // 获取属性上的注解 ConvertStatusToStr
            ConvertEnumField convertEnumFieldAnno = field.getDeclaredAnnotation(ConvertEnumField.class);
            if (convertEnumFieldAnno != null) {
                try {
                    // 处理标注了 ConvertEnumField 注解的字段
                    convertEnumFieldHandler(sourceObj, targetObj, field, convertEnumFieldAnno);
                } catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException e) {
                    throw new RuntimeException(e);
                }
            }
            ConvertDateField convertDateFieldAnno = field.getDeclaredAnnotation(ConvertDateField.class);
            if (convertDateFieldAnno != null) {
                try {
                    // 处理标注了 convertDateField 注解的字段
                    convertDateFieldHandler(sourceObj, targetObj, field, convertDateFieldAnno);
                } catch (NoSuchFieldException | IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }


        });
    }

    /**
     * 处理标注了 convertDateField 注解的字段
     *
     * @param sourceObj            源数据
     * @param targetObj            目标数据
     * @param field                要处理的字段
     * @param convertDateFieldAnno 自定义的注解
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    private static void convertDateFieldHandler(Object sourceObj, Object targetObj, Field field, ConvertDateField convertDateFieldAnno) throws NoSuchFieldException, IllegalAccessException {

        // 获取属性上注解的信息
        String sourceFieldName = convertDateFieldAnno.sourceFieldName();
        String pattern = convertDateFieldAnno.pattern();

        // 从源数据中获取需要转化的属性的值
        Field sourceField = sourceObj.getClass().getDeclaredField(sourceFieldName);
        // 由于这是私有字段,我们需要设置为可访问
        sourceField.setAccessible(true);
        // 字段值
        Object value = sourceField.get(sourceObj);

        // 日期类型格式化
        LocalDateTime dateValue = (LocalDateTime) value;
        String format = dateValue.format(DateTimeFormatter.ofPattern(pattern));

        field.set(targetObj, format);

    }

    /**
     * 处理标注了 ConvertEnumField 注解的字段
     *
     * @param sourceObj            源数据
     * @param targetObj            目标数据
     * @param field                要处理的字段
     * @param convertEnumFieldAnno 自定义的注解
     */
    private static void convertEnumFieldHandler(Object sourceObj, Object targetObj, Field field, ConvertEnumField convertEnumFieldAnno) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {

        // 获取属性上注解定义的枚举类(全限定类名)
        String enumClasName = convertEnumFieldAnno.enumClass().getTypeName();
        // 获取属性上注解定义的实体类的字段名
        String sourceFieldName = convertEnumFieldAnno.sourceFieldName();

        // 从源数据中获取需要转化的属性的值
        Field sourceField = sourceObj.getClass().getDeclaredField(sourceFieldName);
        // 由于这是私有字段,我们需要设置为可访问
        sourceField.setAccessible(true);
        // 字段值
        Object value = sourceField.get(sourceObj);

        // 从枚举中获取枚举信息
        String message = getEnumMessage((int) value, enumClasName);

        field.set(targetObj, message);

    }

    /**
     * 从枚举中获取枚举信息
     *
     * @param status 枚举值
     * @param classPath 枚举信息
     * @return 枚举信息
     * @throws ClassNotFoundException
     */
    private static String getEnumMessage(Integer status, String classPath) throws ClassNotFoundException {

        Object[] enumConstants = Class.forName(classPath).getEnumConstants();
        if (enumConstants == null || enumConstants.length <= 0) {
            return "";
        }

        String str = "";

        for (Object enumConstant : enumConstants) {

            Field codeField = null;
            Field messageField = null;
            try {
                codeField = enumConstant.getClass().getDeclaredField("code");
                messageField = enumConstant.getClass().getDeclaredField("message");
                codeField.setAccessible(true);
                messageField.setAccessible(true);

                int code = (int) codeField.get(enumConstant);
                String message = (String) messageField.get(enumConstant);

                if (status == code) {
                    str = message;
                    break;
                }
            } catch (NoSuchFieldException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }

        return str;
    }

}

测试类:

    @Test
    public void test8() {

        Shop shop = new Shop();

        ShopVo shopVo = JSONObject.parseObject(JSON.toJSONString(shop), ShopVo.class);

        BeanUtil.fieldConvert(shop, shopVo);

        logger.info(shopVo.toString());

    }

测试结果:

ShopVo(id=22222, shopName=test, shopStatusStr=未审核, createTimeStr=2024-04-12, updateTime=null, isDeleted=null)

相关推荐

  1. 定义注解反射实现数据类型转换

    2024-04-13 10:20:03       17 阅读
  2. Redis与定义注解实现重复

    2024-04-13 10:20:03       35 阅读
  3. 基于AOP实现定义注解

    2024-04-13 10:20:03       15 阅读
  4. 定义注解+AOP实现日志记录

    2024-04-13 10:20:03       13 阅读
  5. 定义注解实现Excel 导出

    2024-04-13 10:20:03       7 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-13 10:20:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-13 10:20:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-13 10:20:03       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-13 10:20:03       20 阅读

热门阅读

  1. 博客永久链接与计数

    2024-04-13 10:20:03       13 阅读
  2. Android8.1 MTK平台 修改蓝牙默认名称

    2024-04-13 10:20:03       14 阅读
  3. q @ k运算及att = (q @ k.transpose(-2, -1))含义

    2024-04-13 10:20:03       15 阅读
  4. lambda表达式

    2024-04-13 10:20:03       11 阅读
  5. [yotroy.cool]记一次将SSH公钥添加到GitHub

    2024-04-13 10:20:03       13 阅读
  6. 学习笔记——一些数据转换脚本(Python)

    2024-04-13 10:20:03       12 阅读
  7. 面经学习(湖北航信实习)

    2024-04-13 10:20:03       11 阅读
  8. Django 序列化 上传图片

    2024-04-13 10:20:03       12 阅读
  9. kylin创建 Cube

    2024-04-13 10:20:03       15 阅读
  10. git撤销提交

    2024-04-13 10:20:03       17 阅读
  11. C++力扣Leetcode算法4--排序算法

    2024-04-13 10:20:03       15 阅读