字典转换工具类
package com.example.demo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.util.Assert;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author shanks on 2019-10-25
*/
@Slf4j
public class DataFormatter {
/**
* 翻译当前类中需要翻译的字典值
*
* @param source 待翻译的对象
*/
public static <T> void dataFormatter(T source) {
//判断原对象是否为null
Assert.notNull(source, "待翻译的原对象不能为null");
//获取所有属性并翻译字典
Field[] declaredFields = source.getClass().getDeclaredFields();
//翻译字典:找出所有含有@CacheFormatter的属性
Stream<Field> fieldStream = Arrays.stream(declaredFields)
//排除没有注解@CacheFormatter的字段
.filter(field -> field.isAnnotationPresent(CacheFormat.class));
//翻译
doFormatter(fieldStream, source, source.getClass());
}
/**
* 翻译当前集合类中需要翻译的字典值
*
* @param sources 待翻译的集合对象
*/
public static <T> void dataFormatter(List<T> sources) {
//当翻译的集合为空时,返回空的集合
if (sources == null || sources.isEmpty()) {
return;
}
Class targetClass = sources.get(0).getClass();
//获取所有属性并翻译字典
Field[] declaredFields = targetClass.getDeclaredFields();
//翻译字典:找出所有含有@CacheFormat的属性集合
List<Field> formatterFields = Arrays.stream(declaredFields)
//排除没有注解@CacheFormat的字段
.filter(field -> field.isAnnotationPresent(CacheFormat.class))
.collect(Collectors.toList());
//循环列表(并行操作)
sources.parallelStream().forEach(target -> {
//翻译
doFormatter(formatterFields.stream(), target, targetClass);
});
}
/**
* 把原对象转换成目标类的对象,并翻译目标类的属性字典
* 只针对目标类没有范型或者范型与原对象一样
* @param source 原对象
* @param targetClass 目标类
* @return 目标对象
*/
public static <T> T dataConvert(Object source, Class<T> targetClass) {
Assert.isTrue(source != null && targetClass != null, "原对象或目标class不能为null");
T target = BeanUtils.instantiateClass(targetClass);
//把目标对象的属性设置成原对象中对应的属性
BeanUtils.copyProperties(source, target);
dataFormatter(target);
return target;
}
/**
* 实体属性互转
*
* @param source 原对象
* @param target 目标对象
* @return 目标对象
*/
public static <T> T dataObjConvert(Object source, T target) {
Assert.isTrue(source != null && target != null, "待转换的原对象或目标对象不能为null");
//转换
BeanUtils.copyProperties(source, target);
//翻译
dataFormatter(target);
return target;
}
/**
* 批量把原对象转换成目标对象,并翻译目标对象的属性字典
* 如果想返回指定类型的集合即List的子类,参考{@link HyBeanUtils#dataConverts2}
*
* @param sources 原对象集合
* @param targetClass 目标对象的类
* @return 返回转换后的目标集合
*/
public static <T, E> List<T> dataConverts(List<E> sources, Class<T> targetClass) {
Assert.notNull(targetClass, "转换的目标Class不能为null");
//当翻译的集合为空时,返回空的集合
if (sources == null || sources.isEmpty()) {
List<T> targetList = new ArrayList<>();
return targetList;
}
//获取原集合的类型
Class<? extends List> aClass = sources.getClass();
//目标集合
List targetList = BeanUtils.instantiateClass(aClass);
//把目标对象的属性设置成原对象中对应的属性(并行操作)
sources.parallelStream().forEach(item -> {
T target = BeanUtils.instantiateClass(targetClass);
BeanUtils.copyProperties(item, target);
targetList.add(target);
});
//翻译字典
dataFormatter(targetList);
return targetList;
}
/**
* 返回指定类型的方法,这里的类型必须是List的子类
* 批量把原对象转换成目标对象,并翻译目标对象的属性字典,
*
* @param sources 原对象集合
* @param targetClass 目标对象的类
* @param returnType 返回值类型
* @return 返回转换后的目标集合
*/
public static <T, E, R extends List<T>> R dataConverts2(List<E> sources, Class<T> targetClass, Class<R> returnType) {
Assert.notNull(targetClass, "转换的目标Class不能为null");
Assert.notNull(returnType, "返回值类型Class不能为null");
//当翻译的集合为空时,返回空的集合
if (sources == null || sources.isEmpty()) {
return null;
}
//目标集合
R targetList = BeanUtils.instantiateClass(returnType);
//把目标对象的属性设置成原对象中对应的属性(并行操作)
sources.parallelStream().forEach(item -> {
T target = BeanUtils.instantiateClass(targetClass);
BeanUtils.copyProperties(item, target);
targetList.add(target);
});
//翻译字典
dataFormatter(targetList);
return targetList;
}
/**
* 对目标类需要翻译的字段进行翻译
*
* @param stream
* @param target 目标对象
* @param targetClass 目标对象类
*/
private static <T> void doFormatter(Stream<Field> stream, Object target, Class<T> targetClass) {
//排除目标对象中字段值为null的字段
stream.filter(field -> {
PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(targetClass, field.getName());
Object invoke = null;
try {
invoke = propertyDescriptor.getReadMethod().invoke(target);
} catch (IllegalAccessException e) {
log.warn("待翻译的字段的get是无法访问的", e);
} catch (InvocationTargetException e) {
log.warn("调用待翻译的字段的get方法时报错", e);
} catch (Exception e) {
log.warn("确保属性有get,set方法", e);
}
return invoke != null;
//遍历需要翻译的字段
}).forEach(field -> {
CacheFormat annotation = field.getAnnotation(CacheFormat.class);
//缓存系统编号,如果不指定则默认为当前系统编号
String systemCode = "system_code";
if (StringUtils.isNotBlank(annotation.systemCode())) {
systemCode = annotation.systemCode();
}
//缓存key,如果不指定,则默认为字段名称
String key = annotation.key();
if (StringUtils.isBlank(key)) {
key = field.getName();
}
//判断注解@CacheFormatter是否指定把字典翻译到另一个字段上
String formatterField = annotation.destination();
if (StringUtils.isBlank(formatterField)) {
//当注解中不指定其他字段时,默认翻译到加注解的属性上
formatterField = field.getName();
}
try {
PropertyDescriptor orginPropertyDescriptor = BeanUtils.getPropertyDescriptor(targetClass, field.getName());
Object value = orginPropertyDescriptor.getReadMethod().invoke(target);
//设置目标字段值
PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(targetClass, formatterField);
//取缓存
String cacheValue = RedisUtils.hget(systemCode +":valueset:" + key, value + "");
//如果数据字典中查询不到,则取业务缓存中取
if (StringUtils.isBlank(cacheValue)) {
cacheValue = RedisUtils.hget(systemCode + ":valueset:" + key, value + "");
}
Assert.hasLength(cacheValue, "在缓存" + key + "中没有找到" + value + "对应的缓存");
//设置缓存值到属性字段中
propertyDescriptor.getWriteMethod().invoke(target, cacheValue);
} catch (IllegalAccessException e) {
log.warn("待翻译的字段的set是无法访问的", e);
} catch (InvocationTargetException e) {
log.warn("调用待翻译的字段的set方法时报错", e);
} catch (Exception e) {
log.warn("调用待翻译的字段的set方法时报错,推测类型不匹配", e);
}
});
}
}
注解类
package com.example.demo;
import java.lang.annotation.*;
/**
* @author muyl
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheFormat {
/**
* 缓存key
* @return
*/
String key();
/**
* 指定翻译值存放字段, 例如:userType的翻译结果放到userTypeName上
* @return
*/
String destination() default "";
/**
* 系统编号
* @return
*/
String systemCode() default "";
}
工具类
package com.example.demo;
/**
* @author shanks on 2019-10-25
*/
public class RedisUtils {
public static String hget(String s, String s1) {
return "男";
}
}