BeanUtils——JavaBean相互杏彩平台带保险理赔仓转换及字典翻译

在升级公司架构过程中杏彩平台带保险理赔仓(www.1159880099.com )QQ1159880099,发现有大量Entity与DTO相互转换的问题,并且其中还伴随DTO中的数据字典翻译,所以特意写个工具类,主要利用spring提供的BeanUtils工具类,用redis翻译字典
其中功能包括:

翻译JavaBean中带有@CacheFormat的属性

/**

/**

  • 把原对象转换成目标类的对象,并翻译目标类的属性字典
  • 只针对目标类没有范型或者范型与原对象一样
  • @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

/**

  • 批量把原对象转换成目标对象,并翻译目标对象的属性字典
  • 如果想返回指定类型的集合即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<T> 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转换的升级版 T

/**

  • 返回指定类型的方法,这里的类型必须是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, new Object[]{});
    } catch (IllegalAccessException e) {
    logger.warn("待翻译的字段的get是无法访问的", e);
    } catch (InvocationTargetException e) {
    logger.warn("调用待翻译的字段的get方法时报错", e);
    } catch (Exception e) {
    logger.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, new Object[]{});
        //设置目标字段值
        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) {
        logger.warn("待翻译的字段的set是无法访问的", e);
    } catch (InvocationTargetException e) {
        logger.warn("调用待翻译的字段的set方法时报错", e);
    } catch (Exception e) {
        e.printStackTrace();
        logger.warn("调用待翻译的字段的set方法时报错,推测类型不匹配", e);
    }

    });

}
注解 CacheFormat

@Target(ElementType.FIELD)br/>@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheFormat {

/**

  • 缓存key
  • @return
    */
    String key();

    /**

  • 指定翻译值存放字段, 例如:userType的翻译结果放到userTypeName上
  • @return
    */
    String destination() default "";

    /**

  • 系统编号
  • @return
    */
    String systemCode() default "";
    注意:该翻译只关注第一层即当前对象的属性,并不会递归翻译

比如:当前类有一个属性为对象实例,该对象也有被@CacheFormat注解的属性

这时该工具类不会去翻译这个属性中的属性,需要开发者先用当前工具类转换该属性

然后再设置到目标类中

猜你喜欢

转载自blog.51cto.com/13890914/2151248
今日推荐