zoukankan      html  css  js  c++  java
  • BeanUtils——JavaBean相互转换及字典翻译

    JavaBean相互转换

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

    其中功能包括:

    • 翻译JavaBean中带有@CacheFormat的属性

       /**
        * 翻译当前类中需要翻译的字典值
        *
        * @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());
       }
      
    • 翻译List中带有@CacheFormat的属性

       /**
        * 翻译当前集合类中需要翻译的字典值
        *
        * @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);
           });
      }
      
    • Entity 与DTO互转

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

      /**
       * 批量把原对象转换成目标对象,并翻译目标对象的属性字典
       * 如果想返回指定类型的集合即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与T互转,这里的T是List的子类
      之所以写这个,就是为了解决mybatis的Page转换问题

      /**
       * 返回指定类型的方法,这里的类型必须是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)
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      public @interface CacheFormat {
      
      	/**
      	 *  缓存key
      	 * @return
      	 */
      	String key();
      	
      	/**
      	 * 指定翻译值存放字段, 例如:userType的翻译结果放到userTypeName上
      	 * @return
      	 */
      	String destination() default "";
      	
      	/**
      	 * 系统编号
      	 * @return
      	 */
      	String systemCode() default "";
      

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

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

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

    然后再设置到目标类中

  • 相关阅读:
    计算机编程的24条法则
    爱的十个秘密10.热情的力量
    爱的十个秘密9.承诺的力量
    CPU信息的获得(转自大富翁)
    从编程员的角度理解 NTFS 2000:流和硬链接
    爱的十个秘密8.沟通的力量
    Delphi面向对象编程的20条规则(转自太平洋电脑网)
    程序读取计算机设备管理器中各设备的状态(启用/禁用)?(转自大富翁)
    关于NTFS文件系统中的数据流问题
    Windows新版算号器技术原理
  • 原文地址:https://www.cnblogs.com/liruiloveparents/p/9378661.html
Copyright © 2011-2022 走看看