1. 程式人生 > >BeanUtils——JavaBean相互杏彩平臺帶保險理賠倉轉換及字典翻譯

BeanUtils——JavaBean相互杏彩平臺帶保險理賠倉轉換及字典翻譯

dst ger 時報 filter 操作 oid 一個 script targe

在升級公司架構過程中杏彩平臺帶保險理賠倉(www.1159880099.com )QQ1159880099,發現有大量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的屬性
    br/>//翻譯字典:找出所有含有@CacheFormatter的屬性
    br/>//排除沒有註解@CacheFormatter的字段
    //翻譯
    doFormatter(fieldStream, source, source.getClass());
    }
    翻譯List

    /**

  • 翻譯當前集合類中需要翻譯的字典值
  • @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的屬性集合
    br/>//翻譯字典:找出所有含有@CacheFormat的屬性集合
    br/>//排除沒有註解@CacheFormat的字段
    .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的子類,參考{@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)@Retention(RetentionPolicy.RUNTIME)
br/>@Retention(RetentionPolicy.RUNTIME)
public @interface CacheFormat {

/**

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

    /**

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

    /**

  • 系統編號
  • @return
    */
    String systemCode() default "";
    註意:該翻譯只關註第一層即當前對象的屬性,並不會遞歸翻譯

比如:當前類有一個屬性為對象實例,該對象也有被@CacheFormat註解的屬性

這時該工具類不會去翻譯這個屬性中的屬性,需要開發者先用當前工具類轉換該屬性

然後再設置到目標類中

BeanUtils——JavaBean相互杏彩平臺帶保險理賠倉轉換及字典翻譯