Java对象拷贝时根据枚举自动转换属性值

问题描述:在copyProperties(A,B)拷贝对象时,需要将A.type(Integer)拷贝到B.type(String),但是A.type是类型的数字编码,B.type是类型的描述,通过给B.type加一个注解指定枚举类,实现在拷贝属性的时候,自动转换类型编码到类型描述并赋值B.type;

JDK版本 >= 1.8 

枚举接口定义:

package com.example.visy.test;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author visy.wang
 * @date 2022/1/10 9:43
 */
public interface EnumConvert{
    //将映射关系缓存到Map中,便于快速查找
    Map<String,Object> map = new HashMap<>();

    //key映射道value
    default Object mapping(Object key){
        this.initMap();
        return map.get(String.valueOf(key));
    }

    default void initMap(){
        if(!map.isEmpty()){
            return;
        }
        for (EnumConvert item : getValues()) {
            map.put(String.valueOf(item.getKey()), item.getValue());
        }
    }

    Object getKey();
    // 返回的实际类型必须和目标属性的类型一致
    Object getValue();
    EnumConvert[] getValues();
}

 枚举定义:

package com.example.visy.test;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 方向枚举 (必须实现EnumConvert接口)
 * @author visy.wang
 * @date 2022/1/10 9:42
 */
@Getter
@AllArgsConstructor
public enum DirectionEnum implements EnumConvert {
    TOP(1, "上"),
    DOWN(2, "下"),
    LEFT(3, "左"),
    RIGHT(4, "右");

    private final Integer code;
    private final String desc;

    @Override
    public Object getKey() {
        return code;
    }

    @Override
    public Object getValue() {
        //注意:必须和待转换的目标属性类型相同,否则不会生效
        return desc;
    }

    @Override
    public EnumConvert[] getValues() {
        return DirectionEnum.values();
    }
}

注解定义:

package com.example.visy.test;

import java.lang.annotation.*;
import java.lang.annotation.Target;

/**
 * 枚举转换注解
 * @author visy.wang
 * @date 2022/1/10 10:11
 */
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnumMapper {
    //指定枚举Class
    Class<? extends Enum<?>> value();
    //指定源对象中对应的属性别名(属性名相同时,无需指定alias)
    String alias() default "";
}

 拷贝工具定义:

package com.example.visy.test;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author visy.wang
 * @date 2022/1/10 11:28
 */
public class BeanUtils {
    private static final Map<String, List<Field>> TARGET_FIELD_MAP = new HashMap<>();
    private static final Map<String, Map<String,Field>> SOURCE_FIELD_MAP = new HashMap<>();

    public static void copyProperties(Object source, Object target) throws IllegalAccessException {
        List<Field> targetFields = getFields(target);
        if(Objects.isNull(targetFields) || targetFields.isEmpty()){
            return;
        }

        for(Field targetField: targetFields){
            Field sourceField = getField(source, targetField.getName());

            if(targetField.isAnnotationPresent(EnumMapper.class)){ //检查注解
                EnumMapper enumMapper = targetField.getAnnotation(EnumMapper.class);

                String aliasName = enumMapper.alias();
                if(Objects.nonNull(aliasName) && !aliasName.isEmpty()){
                    //指定了别名的情况,优先使用别名
                    sourceField = getField(source, aliasName);
                }

                if(Objects.isNull(sourceField)){
                    continue; //源属性为空->不拷贝
                }

                if(enumMapper.value().isEnum()){//注解中指定的Class必须是枚举
                    Enum<?> mapper = enumMapper.value().getEnumConstants()[0]; //随便取一个枚举值
                    if(mapper instanceof EnumConvert){//枚举必须实现了EnumConvert接口
                        Object oldValue = sourceField.get(source);
                        //转换后拷贝
                        Object newValue = ((EnumConvert) mapper).mapping(oldValue);
                        if(Objects.nonNull(newValue) && targetField.getType().equals(newValue.getClass())){
                            targetField.set(target, newValue);
                            continue;
                        }
                    }
                }
            }

            if(Objects.isNull(sourceField)){
                continue; //源属性为空->不拷贝
            }

            if(!targetField.getType().equals(sourceField.getType())){
                continue; //两个属性类型不一致->不拷贝
            }

            //拷贝
            targetField.set(target, sourceField.get(source));
        }

    }

    private static List<Field> getFields(Object obj){
        Class<?> clazz = obj.getClass();
        String key = clazz.getName();
        List<Field> fields = TARGET_FIELD_MAP.get(key);
        if(Objects.isNull(fields)){
            Field[] fieldArray = clazz.getDeclaredFields();
            Field.setAccessible(fieldArray, true);
            fields = Arrays.stream(fieldArray).collect(Collectors.toList());
            TARGET_FIELD_MAP.put(key, fields);
        }
        return fields;
    }

    private static Field getField(Object obj, String fieldName){
        Class<?> clazz = obj.getClass();
        String key = clazz.getName();
        Map<String,Field> map = SOURCE_FIELD_MAP.get(key);
        if(Objects.isNull(map)){
            Field[] fieldArray = clazz.getDeclaredFields();
            Field.setAccessible(fieldArray, true);
            map = Arrays.stream(fieldArray).collect(Collectors.toMap(Field::getName, Function.identity()));
            SOURCE_FIELD_MAP.put(key, map);
        }
        return map.get(fieldName);
    }
}

源对象:

package com.example.visy.test;

import lombok.Data;

/**
 * 源对象
 * @author visy.wang
 * @date 2022/1/10 11:26
 */
@Data
public class Source {
    private Integer top = 1;
    private Integer down = 2;
    private Integer left = 3;
    private Integer right = 4;
    private Integer center = 5;

    private String name = "张三";
}

 目标对象:

package com.example.visy.test;

import lombok.Data;

/**
 * 目标对象
 * @author visy.wang
 * @date 2022/1/10 10:11
 */
@Data
public class Target {
    //和Source.top同名
    //Long和枚举的getValue()的实际类型(String)不匹配
    //Long和Source.top类型(Integer)不匹配
    //结果为:null
    @EnumMapper(DirectionEnum.class)
    private Long top;

    //和Source.down同名
    //Integer和枚举的getValue()的实际类型(String)不匹配
    //Integer和Source.down类型(Integer)匹配
    //结果为:2
    @EnumMapper(DirectionEnum.class)
    private Integer down;

    //和Source.left同名
    //String和枚举的getValue()的实际类型(String)匹配
    //结果为:"左"
    @EnumMapper(DirectionEnum.class)
    private String left;

    //和Source.right不同名,但指定了别名"right"
    //String和枚举的getValue()的实际类型(String)匹配
    //结果为:"右"
    @EnumMapper(value = DirectionEnum.class, alias = "right")
    private String rightStr;

    //无需转换的属性只要同名且类型一致就可正常拷贝
    private String name;
}

测试类:

package com.example.visy.test;

/**
 * @author visy.wang
 * @date 2022/1/10 10:47
 */
public class Test {
    public static void main(String[] args) throws Exception{
        Source source = new Source();
        Target target = new Target();

        BeanUtils.copyProperties(source, target);

        System.out.println(target);
    }
}

打印结果:

 Target(top=null, down=2, left=左, rightStr=右, name=张三)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值