概述
通过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);// 获取属性上的注解 ConvertStatusToStrConvertEnumField 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;}}
测试类:
@Testpublic 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)