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

概述

通过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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用 Java 的反射机制和自定义注解实现该功能,以下是一个示例代码: 首先,定义一个自定义注解 `DefaultValue`,用于标识需要设置默认值的属性: ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface DefaultValue { String value() default ""; } ``` 然后,在需要设置默认值的属性上上 `@DefaultValue` 注解: ```java public class User { private String name; @DefaultValue("18") private int age; // 省略 getter 和 setter 方法 } ``` 最后,在后端接收对象的代码中,使用反射机制遍历对象的所有属性,如果某个属性被标记为 `@DefaultValue`,且其值为 `null`,则设置其默认值: ```java public void processUser(User user) throws IllegalAccessException { Class<?> clazz = user.getClass(); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(DefaultValue.class)) { field.setAccessible(true); Object value = field.get(user); if (value == null) { DefaultValue defaultValue = field.getAnnotation(DefaultValue.class); String defaultValueStr = defaultValue.value(); Class<?> fieldType = field.getType(); if (fieldType == int.class || fieldType == Integer.class) { field.set(user, Integer.parseInt(defaultValueStr)); } else if (fieldType == long.class || fieldType == Long.class) { field.set(user, Long.parseLong(defaultValueStr)); } else if (fieldType == float.class || fieldType == Float.class) { field.set(user, Float.parseFloat(defaultValueStr)); } else if (fieldType == double.class || fieldType == Double.class) { field.set(user, Double.parseDouble(defaultValueStr)); } else if (fieldType == boolean.class || fieldType == Boolean.class) { field.set(user, Boolean.parseBoolean(defaultValueStr)); } else { field.set(user, defaultValueStr); } } } } } ``` 以上代码中,我们使用 `field.isAnnotationPresent(DefaultValue.class)` 判断某个属性是否被标记为 `@DefaultValue`,然后使用 `field.setAccessible(true)` 将其设置为可访问,最后使用 `field.get(user)` 获取其值,如果值为 `null`,则使用反射设置该属性的默认值。 需要注意的是,我们需要判断该属性的类型,然后根据类型将默认值转换成相应的类型。以上代码只实现了对基本数据类型和字符串类型的支持,如果需要支持其他类型,可以在代码中添相应的处理逻辑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值