概述
通过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)