JavaBean相互转换

在升级公司架构过程中,发现有大量  Entity与DTO相互转换的问题,并且其中还伴随DTO中的数据字典翻译,所以特意写个工具类,主要利用spring 提供的 BeanUtils工具类,用redis翻译字典

其中功能包括:

  • 翻译JavaBean中带有@CacheFormat的属性
    /**
    * 翻译当前类中需要翻译的字典值

    @param source 待翻译的对象
    */
    public static

         //判断原对象是否为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());
     }
  • 翻译List

         //当翻译的集合为空时,返回空的集合
         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);
         });
    }
  • Entity 与DTO互转
    /**
    * 把原对象转换成目标类的对象,并翻译目标类的属性字典
    * 只针对目标类没有范型或者范型与原对象一样
    * @param source 原对象
    * @param targetClass 目标类
    * @return 目标对象
    */
    public static

        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

        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

        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

        //排除目标对象中字段值为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)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface CacheFormat {

      /**
       *  缓存key
       * @return
       */
      String key();
    
      /**
       * 指定翻译值存放字段, 例如:userType的翻译结果放到userTypeName上
       * @return
       */
      String destination() default "";
    
      /**
       * 系统编号
       * @return
       */
      String systemCode() default "";

注意:该翻译只关注第一层即当前对象的属性,并不会递归翻译

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

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

然后再设置到目标类中

版权声明:本文为liruiloveparents原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/liruiloveparents/p/9378661.html