以下数据转换框架适用场景:
- 中大型项目,需要统一转换逻辑
- 转换规则复杂且多变
- 对开发效率要求高于极致性能
存在以下优点:
- 批量优化:通过BatchConverter接口支持批量查询,避免N+1问题
- 扩展性好:新的转换类型只需实现Converter接口并注册
- 注解驱动:使用方便,代码侵入性低
- AOP自动处理:对业务代码透明,支持多种返回类型
存在可替代方案:MapStruct。相比于MapStruct,当前方案灵活性更高,性能方面弱于MapStruct。
一、注解定义
1、数据转换注解
/**
* 数据转换注解
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataConvert {
/**
* 转换类型
*/
ConvertType type();
/**
* 转换值
* 字典转换 -> 字典编码
* 常量转换 -> 常量值,格式:"{key1:value1,key2:value2,...}",如:"{1:是,0:否}"
* 枚举转换 -> 枚举类路径
*/
String value() default "";
/**
* 转换目标字段名
* 默认:原字段名 + "Str"
* 如果转换对象不存在该名的字段属性,不会报错
*/
String targetField() default "";
/**
* 是否自动转换
* true:AOP自动转换
* false:需要手动调用转换
*/
boolean autoConvert() default true;
/**
* 转换分组
* 用于手动触发时指定分组
*/
String group() default "default";
/**
* 批量转换缓存key
* 相同key的转换会合并为一次批量查询
* 如:a对象中存在 field1、field2,b对象中存在 field3,这三个字段都是部门转换,设置为相同的key则会合并为一次批量查询在转换
*/
String batchKey() default "";
}
2、转换类型枚举
/**
* 转换类型枚举
*/
public enum ConvertType {
/**
* 字典转换
*/
DICT,
/**
* 枚举转换
*/
ENUM,
/**
* 常量转换
*/
CONSTANT,
/**
* 部门
*/
DEPARTMENT,
/**
* 用户
*/
USER,
/**
* 角色
*/
ROLE,
/**
* 自定义转换
*/
CUSTOM
}
二、转换上下文和任务模型
1、批量转换上下文
/**
* 批量转换上下文
*/
@Data
public class BatchConvertContext {
/**
* 所有需要转换的源对象
*/
private List<Object> sources = new ArrayList<>();
/**
* 字段缓存
*/
private final FieldCache fieldCache;
/**
* 转换任务映射
* 结构:转换类型 -> (批量key -> 转换任务)
*/
private Map<ConvertType, Map<String, BatchConvertTask>> tasks = new ConcurrentHashMap<>();
public BatchConvertContext(FieldCache fieldCache) {
this.fieldCache = fieldCache;
}
/**
* 添加转换任务到上下文
*
* @param source 源对象
* @param sourceField 源字段
* @param annotation 转换注解
*/
public void addTask(Object source, Field sourceField, DataConvert annotation) {
sources.add(source);
ConvertType type = annotation.type();
String batchKey = getBatchKey(annotation, sourceField);
// 按类型和批量key分组任务
tasks.computeIfAbsent(type, k -> new ConcurrentHashMap<>())
.computeIfAbsent(batchKey, k -> new BatchConvertTask(type, annotation.value(), batchKey, fieldCache))
.addConvertItem(source, sourceField, annotation);
}
/**
* 生成批量转换的缓存key
*/
private String getBatchKey(DataConvert annotation, Field sourceField) {
if (!annotation.batchKey().isEmpty()) {
return annotation.batchKey();
}
return annotation.type() + ":" + annotation.value();
}
}
2、批量转换任务
/**
* 批量转换任务
*/
@Data
public class BatchConvertTask {
/**
* 转换类型
*/
private ConvertType type;
/**
* 转换参数值
*/
private String convertValue;
/**
* 批量key
*/
private String batchKey;
/**
* 字段缓存
*/
private final FieldCache fieldCache;
/**
* 转换项列表
*/
private List<ConvertItem> items = new ArrayList<>();
public BatchConvertTask(ConvertType type, String convertValue, String batchKey, FieldCache fieldCache) {
this.type = type;
this.convertValue = convertValue;
this.batchKey = batchKey;
this.fieldCache = fieldCache;
}
/**
* 添加转换项
*/
public void addConvertItem(Object source, Field sourceField, DataConvert annotation) {
items.add(new ConvertItem(source, sourceField, annotation));
}
/**
* 获取所有需要转换的源值
*/
public Set<Object> getSourceValues() {
return items.stream()
.map(item -> {
try {
Field sourceField = item.getSourceField();
return sourceField.get(item.getSource());
} catch (IllegalAccessException e) {
throw new RuntimeException("数据转换: 获取字段值失败: " + item.getSourceField().getName(), e);
}
})
.filter(Objects::nonNull)
.collect(Collectors.toSet());
}
}
3、单个转换项
/**
* 单个转换项
*/
@Data
@AllArgsConstructor
public class ConvertItem {
/**
* 源对象
*/
private Object source;
/**
* 源字段
*/
private Field sourceField;
/**
* 转换注解配置
*/
private DataConvert annotation;
}
4、字段缓存
/**
* 字段缓存
* 用于缓存单次转换过程中的字段信息,提升反射性能
*/
public class FieldCache {
/**
* 类字段缓存
* 结构:Class -> (字段名 -> Field)
*/
private final Map<Class<?>, Map<String, Field>> classFieldCache = new ConcurrentHashMap<>();
/**
* 获取类的字段
*/
public Field getField(Class<?> clazz, String fieldName) {
return classFieldCache
.computeIfAbsent(clazz, k -> new ConcurrentHashMap<>())
.computeIfAbsent(fieldName, k -> {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
return null;
}
});
}
/**
* 获取类的所有字段
*/
public Field[] getDeclaredFields(Class<?> clazz) {
Map<String, Field> fieldMap = classFieldCache.get(clazz);
if (fieldMap != null) {
return fieldMap.values().toArray(new Field[0]);
}
// 首次访问,扫描并缓存所有字段
Field[] fields = clazz.getDeclaredFields();
Map<String, Field> newFieldMap = new ConcurrentHashMap<>();
for (Field field : fields) {
field.setAccessible(true);
newFieldMap.put(field.getName(), field);
}
classFieldCache.put(clazz, newFieldMap);
return fields;
}
/**
* 清空缓存
*/
public void clear() {
classFieldCache.clear();
}
}
三、转换器接口和基础实现
1、转换器接口
/**
* 转换器接口
* 所有转换器都需要实现此接口
*/
public interface Converter {
/**
* 判断是否支持该转换类型
*/
boolean supports(ConvertType type);
/**
* 单条数据转换
*
* @param source 源对象
* @param sourceField 源字段
* @param targetField 目标字段名
* @param convertValue 转换参数值
*/
void convert(Object source, Field sourceField, String targetField, String convertValue);
}
2、批量转换器接口
/**
* 批量转换器接口
* 支持批量优化的转换器需要实现此接口
*/
public interface BatchConverter extends Converter {
/**
* 批量转换
*
* @param task 批量转换任务
*/
void batchConvert(BatchConvertTask task);
/**
* 是否支持批量转换
*
* @return true-支持,false-不支持
*/
default boolean supportBatch() {
return true;
}
}
四、具体转换器实现(示例)
1、字典转换器
/**
* 字典转换器
*/
@Slf4j
@Component
public class DictConverter implements BatchConverter {
//字典服务
@Resource
private DictServer dictServer;
@Override
public boolean supports(ConvertType type) {
return ConvertType.DICT == type;
}
@Override
public void convert(Object source, Field sourceField, String targetField, String dictType) {
// 单条转换实现
try {
Object sourceValue = sourceField.get(source);
if (sourceValue != null) {
SysDictionaryItem dictItem = dictServer.byTypeValue(dictType, sourceValue.toString());
ConverterUtil.setTargetFieldValue(source, targetField, dictItem != null ? dictItem.getDiName() : "未知");
}
} catch (Exception e) {
log.error("数据转换: 字典.单条转换失败, 字段: {}, 字典类型: {}", sourceField.getName(), dictType, e);
throw new MyInmException("数据转换: 字典.转换失败");
}
}
@Override
public void batchConvert(BatchConvertTask task) {
String dictType = task.getConvertValue();
Map<String, SysDictionaryItem> dictMap = MapUtil.listToMap(
dictServer.listByCodes(Collections.singletonList(dictType)),
SysDictionaryItem::getDiValue
);
FieldCache fieldCache = task.getFieldCache();
// 批量设置结果
for (ConvertItem item : task.getItems()) {
try {
Field sourceField = item.getSourceField();
Object sourceValue = sourceField.get(item.getSource());
if (sourceValue != null) {
String targetField = ConverterUtil.getTargetField(sourceField, item.getAnnotation());
SysDictionaryItem dictItem = dictMap.get(sourceValue.toString());
ConverterUtil.setTargetFieldValue(item.getSource(), targetField,
dictItem != null ? dictItem.getDiName() : "未知",
fieldCache);
}
} catch (Exception e) {
log.warn("数据转换: 字典.转换失败: {}", e.getMessage());
}
}
}
}
2、枚举转换器
/**
* 枚举转换器
*/
@Slf4j
@Component
public class EnumConverter implements BatchConverter {
// 枚举映射关系
private final Map<String, Map<String, String>> enumMappingCache = new ConcurrentHashMap<>();
// 优先查找的字段名
// 根据 VALUE_FIELD_NAMES 枚举值匹配,转换成 DISPLAY_FIELD_NAMES 枚举值
// 如果枚举类中不存在以下字段则不作转换
private static final String[] VALUE_FIELD_NAMES = {"value", "code"};
private static final String[] DISPLAY_FIELD_NAMES = {"name", "label"};
@Override
public boolean supports(ConvertType type) {
return ConvertType.ENUM == type;
}
@Override
public void convert(Object source, Field sourceField, String targetField, String enumClassPath) {
try {
Object sourceValue = sourceField.get(source);
if (sourceValue == null) {
return;
}
Map<String, String> mapping = getCachedEnumMapping(enumClassPath);
String displayName = mapping.get(sourceValue.toString());
ConverterUtil.setTargetFieldValue(source, targetField,
displayName != null ? displayName : "未知");
} catch (IllegalAccessException e) {
log.error("数据转换: 枚举.单条转换失败, 字段: {}, 枚举类: {}", sourceField.getName(), enumClassPath, e);
throw new MyInmException("数据转换: 枚举.转换失败");
}
}
@Override
public void batchConvert(BatchConvertTask task) {
String enumClassPath = task.getConvertValue();
FieldCache fieldCache = task.getFieldCache();
// 获取缓存的枚举映射
Map<String, String> enumMapping = getCachedEnumMapping(enumClassPath);
// 批量设置结果
for (ConvertItem item : task.getItems()) {
try {
Field sourceField = item.getSourceField();
Object sourceValue = sourceField.get(item.getSource());
if (sourceValue == null) {
return;
}
String targetField = ConverterUtil.getTargetField(sourceField, item.getAnnotation());
String displayName = enumMapping.get(sourceValue.toString());
ConverterUtil.setTargetFieldValue(item.getSource(), targetField,
displayName != null ? displayName : "未知", fieldCache);
} catch (Exception e) {
log.warn("数据转换: 枚举.转换失败, 源字段: {}, 错误: {}",
item.getSourceField().getName(), e.getMessage());
}
}
}
/**
* 获取缓存的枚举映射,如果不存在则构建
*/
private Map<String, String> getCachedEnumMapping(String enumClassPath) {
return enumMappingCache.computeIfAbsent(enumClassPath, this::buildEnumMapping);
}
/**
* 构建枚举值到显示名称的映射
*/
private Map<String, String> buildEnumMapping(String enumClassPath) {
Map<String, String> mapping = new ConcurrentHashMap<>();
try {
Class<?> enumClass = Class.forName(enumClassPath);
if (!enumClass.isEnum()) {
log.warn("数据转换: 枚举.类不是枚举类型: {}", enumClassPath);
return mapping;
}
Arrays.stream(enumClass.getEnumConstants())
.forEach(enumConstant -> processEnumConstant(enumConstant, mapping));
} catch (ClassNotFoundException e) {
log.error("数据转换: 枚举.类未找到: {}", enumClassPath, e);
} catch (Exception e) {
log.error("数据转换: 枚举.构建映射失败: {}", enumClassPath, e);
}
return mapping;
}
/**
* 处理单个枚举常量
*/
private void processEnumConstant(Object enumConstant, Map<String, String> mapping) {
try {
String enumValue = getEnumFieldValue(enumConstant, VALUE_FIELD_NAMES);
String displayName = getEnumFieldValue(enumConstant, DISPLAY_FIELD_NAMES);
if (enumValue != null) {
mapping.put(enumValue, displayName);
}
// 同时将枚举名称作为key映射
String enumName = ((Enum<?>) enumConstant).name();
mapping.put(enumName, displayName);
} catch (Exception e) {
log.warn("数据转换: 枚举.处理枚举常量失败: {}", enumConstant, e);
}
}
/**
* 获取枚举指定字段的值
*/
private String getEnumFieldValue(Object enumConstant, String[] fieldNames) {
// 优先尝试指定字段
for (String fieldName : fieldNames) {
try {
Field field = enumConstant.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
Object value = field.get(enumConstant);
if (value != null) {
return value.toString();
}
} catch (NoSuchFieldException e) {
// 字段不存在,继续尝试下一个
} catch (Exception e) {
log.debug("数据转换: 枚举.获取字段[{}]值失败: {}", fieldName, e.getMessage());
}
}
// 所有指定字段都不存在或为null,返回枚举名称
return ((Enum<?>) enumConstant).name();
}
}
3、常量转换器
/**
* 常量转换器
*/
@Slf4j
@Component
public class ConstantConverter implements BatchConverter {
// 常量映射关系
private final Map<String, Map<String, String>> constantCache = new ConcurrentHashMap<>();
@Override
public boolean supports(ConvertType type) {
return ConvertType.CONSTANT == type;
}
@Override
public void convert(Object source, Field sourceField, String targetField, String constantMapping) {
try {
Object sourceValue = sourceField.get(source);
if (sourceValue == null) {
return;
}
Map<String, String> mapping = getCachedConstantMapping(constantMapping);
String displayValue = mapping.get(sourceValue.toString());
ConverterUtil.setTargetFieldValue(source, targetField,
displayValue != null ? displayValue : "未知");
} catch (IllegalAccessException e) {
log.error("数据转换: 常量.单条转换失败, 字段: {}, 常量映射: {}", sourceField.getName(), constantMapping, e);
throw new MyInmException("数据转换: 常量.转换失败");
}
}
@Override
public void batchConvert(BatchConvertTask task) {
String constantMapping = task.getConvertValue();
FieldCache fieldCache = task.getFieldCache();
// 获取缓存的常量映射
Map<String, String> mapping = getCachedConstantMapping(constantMapping);
// 批量设置结果
for (ConvertItem item : task.getItems()) {
try {
Field sourceField = item.getSourceField();
Object sourceValue = sourceField.get(item.getSource());
if (sourceValue == null) {
return;
}
String targetField = ConverterUtil.getTargetField(sourceField, item.getAnnotation());
String displayValue = mapping.get(sourceValue.toString());
ConverterUtil.setTargetFieldValue(item.getSource(), targetField,
displayValue != null ? displayValue : "未知", fieldCache);
} catch (Exception e) {
log.warn("数据转换: 常量.转换失败, 源字段: {}, 错误: {}",
item.getSourceField().getName(), e.getMessage());
}
}
}
/**
* 获取缓存的常量映射,如果不存在则解析
*/
private Map<String, String> getCachedConstantMapping(String constantMapping) {
return constantCache.computeIfAbsent(constantMapping, this::parseConstantMapping);
}
/**
* 解析常量映射字符串
* 格式:"{key1:value1,key2:value2,...}",如:"{1:是,0:否}"
*/
private Map<String, String> parseConstantMapping(String constantMapping) {
if (!StringUtils.hasText(constantMapping)) {
log.warn("数据转换: 常量.映射字符串为空");
return Collections.emptyMap();
}
// 移除首尾的大括号
String mappingStr = constantMapping.trim();
if (mappingStr.startsWith("{") && mappingStr.endsWith("}")) {
mappingStr = mappingStr.substring(1, mappingStr.length() - 1).trim();
}
if (!StringUtils.hasText(mappingStr)) {
log.warn("数据转换: 常量.映射内容为空");
return Collections.emptyMap();
}
Map<String, String> mapping = new HashMap<>();
try {
// 按逗号分割键值对
String[] keyValuePairs = mappingStr.split(",");
for (String pair : keyValuePairs) {
if (!StringUtils.hasText(pair)) {
continue;
}
// 按冒号分割键值
String[] keyValue = pair.split(":", 2);
if (keyValue.length != 2) {
log.warn("数据转换: 常量.无效的键值对格式: {}", pair);
continue;
}
String key = keyValue[0].trim();
String value = keyValue[1].trim();
if (StringUtils.hasText(key) && StringUtils.hasText(value)) {
mapping.put(key, value);
}
}
log.debug("数据转换: 常量.解析映射成功, 条目数: {}", mapping.size());
} catch (Exception e) {
log.error("数据转换: 常量.解析映射失败: {}", constantMapping, e);
}
return mapping;
}
}
五、转换管理器
/**
* 转换管理器
*/
@Slf4j
@Component
public class ConvertManager {
@Autowired
private List<Converter> converters;
/**
* 转换器映射缓存
*/
private final Map<ConvertType, Converter> converterMap = new ConcurrentHashMap<>();
/**
* 初始化方法
* 扫描所有转换器并建立映射关系
*/
@PostConstruct
public void init() {
log.info("数据转换: 初始化转换管理器...");
for (Converter converter : converters) {
for (ConvertType type : ConvertType.values()) {
if (converter.supports(type)) {
converterMap.put(type, converter);
log.debug("数据转换: 注册转换器: {} -> {}", type, converter.getClass().getSimpleName());
break;
}
}
}
log.info("数据转换: 转换管理器初始化完成,共注册 {} 个转换器", converterMap.size());
}
/**
* 根据转换类型获取对应的转换器
*/
public Converter getConverter(ConvertType type) {
return converterMap.get(type);
}
/**
* 检查是否支持该转换类型
*/
public boolean hasConverter(ConvertType type) {
return converterMap.containsKey(type);
}
}
六、转换助手
/**
* 数据转换助手
*/
@Slf4j
@Component
public class DataConvertHelper {
@Autowired
private ConvertManager convertManager;
/**
* 转换单个对象
*
* @param data 需要转换的数据
*/
public void convert(Object data) {
if (data == null) {
log.debug("数据转换: 转换对象为空");
return;
}
// 单个对象使用列表方式处理
batchConvertList(Collections.singletonList(data));
}
/**
* 转换对象列表
* 对列表数据进行批量转换优化
*
* @param list 需要转换的对象列表
*/
public void batchConvertList(List<?> list) {
if (list == null || list.isEmpty()) {
log.debug("数据转换: 转换列表为空");
return;
}
log.debug("数据转换: 开始处理 {} 个对象", list.size());
FieldCache fieldCache = new FieldCache();
try {
// 构建转换上下文
BatchConvertContext context = buildConvertContext(list, fieldCache);
if (context.getTasks().isEmpty()) {
log.debug("数据转换: 未发现需要转换的字段");
return;
}
// 执行转换
executeBatchConvert(context);
log.debug("数据转换: 完成所有转换任务");
} finally {
fieldCache.clear();
}
}
/**
* 构建转换上下文
* 扫描所有对象的转换需求并进行分组
*/
private BatchConvertContext buildConvertContext(List<?> list, FieldCache fieldCache) {
BatchConvertContext context = new BatchConvertContext(fieldCache);
for (Object obj : list) {
if (obj == null) continue;
// 缓存的字段信息
Field[] fields = fieldCache.getDeclaredFields(obj.getClass());
for (Field field : fields) {
DataConvert annotation = field.getAnnotation(DataConvert.class);
if (isConvertible(annotation)) {
context.addTask(obj, field, annotation);
log.trace("数据转换: 添加转换任务: {}.{}", obj.getClass().getSimpleName(), field.getName());
}
}
}
return context;
}
/**
* 判断字段是否需要转换
*/
private boolean isConvertible(DataConvert annotation) {
return annotation != null && annotation.autoConvert();
}
/**
* 执行批量转换
* 按转换类型分组执行,支持批量优化
*/
private void executeBatchConvert(BatchConvertContext context) {
for (Map.Entry<ConvertType, Map<String, BatchConvertTask>> typeEntry : context.getTasks().entrySet()) {
ConvertType type = typeEntry.getKey();
Converter converter = convertManager.getConverter(type);
if (converter == null) {
log.warn("数据转换: 未找到类型 {} 对应的转换器", type);
continue;
}
// 处理该类型的所有任务
for (BatchConvertTask task : typeEntry.getValue().values()) {
processConvertTask(converter, task);
}
}
}
/**
* 处理单个转换任务
* 优先使用批量转换,不支持时降级为单条转换
*/
private void processConvertTask(Converter converter, BatchConvertTask task) {
if (converter instanceof BatchConverter && ((BatchConverter) converter).supportBatch()) {
// 使用批量转换
log.debug("数据转换: 批量转换处理任务: {}, 项目数: {}", task.getBatchKey(), task.getItems().size());
((BatchConverter) converter).batchConvert(task);
} else {
// 降级为单条转换
log.debug("数据转换: 单条转换处理任务: {}, 项目数: {}", task.getBatchKey(), task.getItems().size());
for (ConvertItem item : task.getItems()) {
converter.convert(
item.getSource(),
item.getSourceField(),
ConverterUtil.getTargetField(item.getSourceField(), item.getAnnotation()),
item.getAnnotation().value()
);
}
}
}
}
七、AOP切面配置
/**
* 数据转换切面
* 自动拦截Controller返回结果并进行数据转换
*/
@Slf4j
@Aspect
@Component
public class DataConvertAspect {
@Autowired
private DataConvertHelper dataConvertHelper;
/**
* 切入点定义:所有Controller类的方法
*/
@Pointcut("within(@org.springframework.stereotype.Controller *) || " +
"within(@org.springframework.web.bind.annotation.RestController *)")
public void controllerPointcut() {
}
/**
* 环绕通知
* 在Controller方法执行后对返回结果进行数据转换
*/
@Around("controllerPointcut()")
public Object aroundController(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();
if (result != null) {
try {
convertResult(result);
} catch (Exception e) {
log.error("数据转换: 数据转换失败", e);
}
}
return result;
}
/**
* 转换返回结果
* 根据结果类型进行相应的转换处理
*/
private void convertResult(Object result) {
if (result instanceof R) {
// 统一响应结果封装
convertResponseResult((R<?>) result);
} else if (result instanceof List) {
// 列表结果
dataConvertHelper.batchConvertList((List<?>) result);
} else if (result instanceof IPage) {
// 分页结果
convertPageResult((IPage<?>) result);
} else {
// 单个对象结果
convertSingleObject(result);
}
}
/**
* 转换统一响应结果
*/
private void convertResponseResult(R<?> responseResult) {
Object data = responseResult.getData();
if (data != null) {
convertResponseData(data);
}
}
/**
* 转换响应数据
*/
private void convertResponseData(Object data) {
if (data instanceof List) {
dataConvertHelper.batchConvertList((List<?>) data);
} else if (data instanceof IPage) {
convertPageResult((IPage<?>) data);
} else {
convertSingleObject(data);
}
}
/**
* 转换分页结果
*/
private void convertPageResult(IPage<?> page) {
if (page != null && page.getRecords() != null) {
dataConvertHelper.batchConvertList(page.getRecords());
}
}
/**
* 转换单个对象
*/
private void convertSingleObject(Object obj) {
dataConvertHelper.convert(obj);
}
}
八、工具类
/**
* 数据转换工具类,供具体转换器使用
*/
@Slf4j
public class ConverterUtil {
/**
* 设置目标字段值
*/
public static void setTargetFieldValue(Object obj, String fieldName, Object value, FieldCache fieldCache) {
try {
Field targetField = fieldCache.getField(obj.getClass(), fieldName);
if (targetField != null) {
targetField.set(obj, value);
} else {
// 目标字段不存在时忽略,可能是可选字段
log.debug("目标字段不存在: {}.{}", obj.getClass().getSimpleName(), fieldName);
}
} catch (IllegalAccessException e) {
throw new RuntimeException("设置字段值失败: " + fieldName, e);
}
}
/**
* 设置目标字段值
*/
public static void setTargetFieldValue(Object obj, String fieldName, Object value) {
try {
Field targetField = obj.getClass().getDeclaredField(fieldName);
targetField.setAccessible(true);
targetField.set(obj, value);
} catch (NoSuchFieldException e) {
// 目标字段不存在时忽略
} catch (IllegalAccessException e) {
throw new RuntimeException("设置字段值失败: " + fieldName, e);
}
}
/**
* 获取目标字段名
* 根据注解配置生成目标字段名
*/
public static String getTargetField(Field sourceField, DataConvert annotation) {
if (!annotation.targetField().isEmpty()) {
return annotation.targetField();
}
return sourceField.getName() + "Str";
}
}
/**
* map工具类
*/
public class MapUtil {
/**
* List 转成 Map
*
* @param list list数据
* @param keyMapper 取主键的 Lambda
* @param <T> VO 类型
* @param <K> 主键类型
* @return Map<主键, VO>
*/
public static <T, K> Map<K, T> listToMap(List<T> list, Function<T, K> keyMapper) {
if (CollectionUtils.isEmpty(list)) {
return Collections.emptyMap();
}
return list.stream().collect(Collectors.toMap(
keyMapper,
Function.identity(),
(oldVal, newVal) -> oldVal));
}
}
九、使用说明及示例
部分代码需要根据实际进行调整,如返回包装类R、字典服务、字典转换器实现等。
注意:aop需启用aop支持
实体使用示例
/**
* 用户视图对象
* 演示数据转换注解的使用
*/
@Data
public class UserVO {
private Long id;
private String username;
/**
* 用户状态 - 字典转换
* 将状态码转换为状态名称
*/
@DataConvert(type = ConvertType.DICT, value = "user_status")
private Integer status;
private String statusStr; // 自动转换的目标字段
/**
* 性别 - 枚举转换
* 将性别编码转换为性别名称
* 这里可以在注解中加一个字段,用于保存枚举类,不使用类路径的方式
*/
@DataConvert(type = ConvertType.ENUM, value = "com.example.enums.GenderEnum")
private Integer gender;
private String genderStr;
/**
* 部门ID - 部门转换
* 将部门ID转换为部门名称
*/
@DataConvert(type = ConvertType.DEPARTMENT, targetField = "deptName")
private Long deptId;
private String deptName;
/**
* 创建人 - 用户转换(手动触发)
* 需要手动调用转换,不自动转换
*/
@DataConvert(type = ConvertType.USER, targetField = "creatorName",
autoConvert = false, group = "detail")
private Long creatorId;
private String creatorName;
/**
* 角色ID - 角色转换
* 将角色ID转换为角色名称
*/
@DataConvert(type = ConvertType.ROLE, targetField = "roleName")
private Long roleId;
private String roleName;
}

被折叠的 条评论
为什么被折叠?



