从反射到字节码:Java22 ClassFile API在Bean属性访问与拷贝中的降维打击


核心原理:解析需要访问的类,将get和set方法使用ClassFile Api动态生成BeanVisitors,实现高性能动态属性访问和属性设置。本文基于Java 23,现在还是预览api,正式版可能会有所调整。

一、BeanVisitors实现属性操作

// 动态生成类的父类定义
@Getter
@RequiredArgsConstructor
public abstract class BeanVisitors {

    /**
     * bean字段对应的字段类型
     */
    private final Map<String, BeanFieldType> beanFieldTypeMap;

    /**
     * 从指定实例中获取指定字段的属性值
     *
     * @param instances 实例
     * @param key       字段名
     * @return 字段值
     */
    public abstract Object get(Object instances, String key);

    /**
     * 设置指定实例中对应字段的属性值
     *
     * @param instances 实例
     * @param key       字段名
     * @param value     字段值
     */
    public abstract void put(Object instances, String key, Object value);

    /**
     * 获取字段类型
     *
     * @param key 字段名
     * @return 字段类型
     */
    public BeanFieldType beanFieldType(String key) {
        return beanFieldTypeMap.get(key);
    }
}

// 简单BeanMap实现
@RequiredArgsConstructor
public final class BeanMap {

    /**
     * 实例
     */
    private final Object instance;

    /**
     * BeanVisitors
     */
    private final BeanVisitors visitors;

    /**
     * @param key 字段名
     * @return 属性值
     */
    public Object get(String key) {
        return visitors.get(instance, key);
    }

    /**
     * @param key   字段名
     * @param value 属性值
     */
    public void put(String key, Object value) {
        visitors.put(instance, key, value);
    }

    /**
     * 将属性copy到对应对象
     *
     * @param target 目标对象
     * @return target
     */
    public <R> R copyTo(R target) {
        return BeanCopier.copy(instance, target);
    }
}

// 字段类型
public class BeanFieldType {

    /**
     * set方法的参数类型
     */
    Class<?> paramClass;

    /**
     * get方法的返回类型
     */
    Class<?> returnClass;

    /**
     * 原始类型转换成包装类
     *
     * @param paramClass 参数类型
     */
    void setParamClass(Class<?> paramClass) {
        this.paramClass = toWrapperClass(paramClass);
    }

    /**
     * 原始类型转换成包装类型
     *
     * @param returnClass 返回类型
     */
    void setReturnClass(Class<?> returnClass) {
        this.returnClass = toWrapperClass(returnClass);
    }

    /**
     * 将基本类型转换成包装类(统一使用包装类处理)
     *
     * @param type 类型
     * @return 包装类
     */
    private Class<?> toWrapperClass(Class<?> type) {
        if (type == int.class) {
            return Integer.class;
        } else if (type == long.class) {
            return Long.class;
        } else if (type == float.class) {
            return Float.class;
        } else if (type == double.class) {
            return Double.class;
        } else if (type == short.class) {
            return Short.class;
        } else if (type == byte.class) {
            return Byte.class;
        } else if (type == char.class) {
            return Character.class;
        } else if (type == boolean.class) {
            return Boolean.class;
        }
        return type;
    }
}

@SuppressWarnings("preview")
public abstract class BeanVisitorFactory {

    /**
     * class对应的访问器缓存(使用软引用来淘汰缓存)
     */
    private static final Map<Class<?>, KeySoftReference> BEAN_MAP_CACHE = new ConcurrentHashMap<>();

    /**
     * 引用队列
     */
    private static final ReferenceQueue<BeanVisitors> KEY_REFERENCE_QUEUE = new ReferenceQueue<>();

    static {
        // 开启线程清除无效缓存
        Thread beanVisitorFactoryClean = new Thread(() -> {
            try {
                while (true) {
                    // 删除map中缓存的无效对象
                    KeySoftReference remove = (KeySoftReference) KEY_REFERENCE_QUEUE.remove();
                    BEAN_MAP_CACHE.remove(remove.key, remove);
                }
            } catch (InterruptedException e) {
                // ignore
            }
        }, "BeanVisitorFactoryClean");
        beanVisitorFactoryClean.setDaemon(true);
        beanVisitorFactoryClean.start();
    }

    /**
     * 根据指定实例创建beanMap
     *
     * @param instance 实例
     * @return beanMap
     */
    public static BeanMap createBeanMap(Object instance) {
        BeanVisitors visitor = createBeanVisitor(instance.getClass());
        return new BeanMap(instance, visitor);
    }

    /**
     * 创建BeanVisitors
     *
     * @param clazz class
     * @return BeanVisitors
     */
    public static BeanVisitors createBeanVisitor(Class<?> clazz) {
        return BEAN_MAP_CACHE.compute(clazz, (entityClass, oldReference) -> {
            if (oldReference != null && oldReference.get() != null) {
                return oldReference;
            }
            // get set分别处理 支持只存在其中一种方法的情况
            GetSetMethod getSetMethod = methodInfo(entityClass);
            String entityClassName = entityClass.getName();
            // 动态生成的class类名
            String beanVisitorsClassName = entityClassName + "$$BeanVisitors";
            byte[] beanVisitorsCode = ClassFile.of().build(ClassDesc.of(beanVisitorsClassName), classBuilder -> {
                // 设置类PUBLIC和SYNTHETIC
                classBuilder.withFlags(AccessFlag.PUBLIC, AccessFlag.SYNTHETIC);
                // 设置父类
                ClassDesc superClassDesc = BeanVisitors.class.describeConstable().orElseThrow();
                classBuilder.withSuperclass(superClassDesc);
                // 实现构造函数
                classBuilder.withMethodBody(ConstantDescs.INIT_NAME, MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_Map),
                        ClassFile.ACC_PUBLIC, codeBuilder -> {
                            // 加载this
                            codeBuilder.aload(codeBuilder.receiverSlot());
                            // 加载第一个参数
                            codeBuilder.aload(codeBuilder.parameterSlot(0));
                            // 执行父类构造方法
                            MethodTypeDesc voidMapMethodTypeDesc = MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_Map);
                            codeBuilder.invokespecial(superClassDesc, ConstantDescs.INIT_NAME, voidMapMethodTypeDesc);
                            // return
                            codeBuilder.return_();
                        });
                ClassDesc entityDesc = ClassDesc.of(entityClassName);
                // 实现get方法
                classBuilder.withMethodBody("get", MethodTypeDesc.of(ConstantDescs.CD_Object, ConstantDescs.CD_Object,
                                ConstantDescs.CD_String), ClassFile.ACC_PUBLIC,
                        codeBuilder -> buildCode(entityDesc, codeBuilder, 3, getSetMethod.hashGetMap,
                                ((blockCodeBuilder, getMethod) -> {
                                    // 调用对应实体的getXX方法
                                    blockCodeBuilder.invokevirtual(entityDesc, getMethod.methodName, MethodTypeDesc.of(getMethod.returnType));
                                    // 将原型类包装成包装类
                                    box(codeBuilder, getMethod.returnType);
                                    // return obj
                                    blockCodeBuilder.areturn();
                                })));
                // 实现put方法
                classBuilder.withMethodBody("put", MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_Object,
                                ConstantDescs.CD_String, ConstantDescs.CD_Object), ClassFile.ACC_PUBLIC,
                        codeBuilder -> buildCode(entityDesc, codeBuilder, 4, getSetMethod.hashSetMap,
                                ((blockCodeBuilder, setMethod) -> {
                                    // 加载第二个参数
                                    blockCodeBuilder.aload(blockCodeBuilder.parameterSlot(2));
                                    // 类型转换
                                    checkCast(blockCodeBuilder, setMethod.paramType);
                                    // 执行对应的setXX方法
                                    blockCodeBuilder.invokevirtual(entityDesc, setMethod.methodName, MethodTypeDesc.of(setMethod.returnType, setMethod.paramType));
                                    // return
                                    blockCodeBuilder.return_();
                                })));
            });
            try {
                // 定义隐藏类
                Class<?> beanVisitorsClass = MethodHandles.privateLookupIn(clazz, MethodHandles.lookup())
                        .defineHiddenClass(beanVisitorsCode, false).lookupClass();
                // 获取构造方法
                Constructor<?> declaredConstructor = beanVisitorsClass.getDeclaredConstructor(Map.class);
                // 实例化
                return new KeySoftReference(entityClass, (BeanVisitors) declaredConstructor
                        .newInstance(Collections.unmodifiableMap(getSetMethod.fieldTypeMap)));
            } catch (IllegalAccessException | NoSuchMethodException | InstantiationException |
                     InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        }).get();
    }

    /**
     * 构建get或者put方法的方法体
     * 字段生成的代码逻辑:
     * <pre>
     * {@code
     * switch (字段名.hashCode) {
     *             case 1:
     *                 if (key.equals(fieldName)) {
     *                   实例.(getXX() | setXX())
     *                   return;
     *                 }
     *                 break;
     *         }
     * }
     * </pre>
     *
     * @param entityDesc     实例描述
     * @param codeBuilder    codeBuilder
     * @param entityStoreIdx 转换类型后实例store的索引
     * @param switchMap      构建switch表达式的map
     * @param methodConsumer get和put分别的额外逻辑
     */
    private static <METHOD extends MethodInfo> void buildCode(ClassDesc entityDesc, CodeBuilder codeBuilder, int entityStoreIdx, Map<Integer, List<METHOD>> switchMap, BiConsumer<CodeBuilder.BlockCodeBuilder, METHOD> methodConsumer) {
        // 存在的时候构建switch
        if (!switchMap.isEmpty()) {
            // 加载参数0
            codeBuilder.aload(codeBuilder.parameterSlot(0));
            // 类型转换
            codeBuilder.checkcast(entityDesc);
            // store
            codeBuilder.astore(entityStoreIdx);
            // 加载参数1
            codeBuilder.aload(codeBuilder.parameterSlot(1));
            // 执行hashcode方法
            codeBuilder.invokevirtual(ConstantDescs.CD_String, "hashCode", MethodTypeDesc.of(ConstantDescs.CD_int));
            int size = switchMap.size();
            // 初始化每个switch对应label 标记每个case的位置
            Map<Integer, Label> hashLabel = HashMap.newHashMap(size);
            List<SwitchCase> switchCases = new ArrayList<>(size);
            for (Integer hashCode : switchMap.keySet()) {
                Label label = codeBuilder.newLabel();
                hashLabel.put(hashCode, label);
                // case hashCode(类中字段名的)
                switchCases.add(SwitchCase.of(hashCode, label));
            }
            // switch结束label
            Label switchEndLabel = codeBuilder.newLabel();
            // 创建switch
            codeBuilder.lookupswitch(switchEndLabel, switchCases);
            for (Map.Entry<Integer, List<METHOD>> hashMethodMap : switchMap.entrySet()) {
                // block代码块
                codeBuilder.block(blockCodeBuilder -> {
                    Integer hashCode = hashMethodMap.getKey();
                    // 获取之前初始化的label 绑定label
                    blockCodeBuilder.labelBinding(hashLabel.get(hashCode));
                    // 处理每一个get或者set方法
                    for (METHOD method : hashMethodMap.getValue()) {
                        // 加载第一个方法参数
                        blockCodeBuilder.aload(blockCodeBuilder.parameterSlot(1));
                        // 加载字段名常量
                        blockCodeBuilder.ldc(method.fieldName());
                        // 执行equals方法 if(key.equals("xx"))
                        blockCodeBuilder.invokevirtual(ConstantDescs.CD_String, "equals", MethodTypeDesc
                                .of(ConstantDescs.CD_boolean, ConstantDescs.CD_Object));
                        // 不满足直接跳转到switch结束
                        blockCodeBuilder.ifeq(switchEndLabel);
                        // 加载之前转换后的实体
                        blockCodeBuilder.aload(entityStoreIdx);
                        // 执行get和put方法的不同逻辑
                        methodConsumer.accept(blockCodeBuilder, method);
                    }
                });
            }
            // switch结束
            codeBuilder.labelBinding(switchEndLabel);
        }
        // 没有找到匹配的 抛出异常
        ClassDesc exClassDesc = UnsupportedOperationException.class.describeConstable().orElseThrow();
        // new UnsupportedOperationException
        codeBuilder.new_(exClassDesc);
        // 将操作数栈上的UnsupportedOperationException复制一份
        codeBuilder.dup();
        // 执行构造方法
        codeBuilder.invokespecial(exClassDesc, ConstantDescs.INIT_NAME, ConstantDescs.MTD_void);
        // 抛出异常
        codeBuilder.athrow();
    }

    /**
     * 获取实体类中的get和set方法信息
     *
     * @param entityClass 实体类
     * @return get和set的方法信息
     */
    private static GetSetMethod methodInfo(Class<?> entityClass) {
        Map<Integer, List<GetMethod>> hashGetMethodMap = new HashMap<>();
        Map<Integer, List<SetMethod>> hashSetMethodMap = new HashMap<>();
        Map<String, BeanFieldType> fieldTypeMap = new HashMap<>();
        GetSetMethod getSetMethod = new GetSetMethod(hashGetMethodMap, hashSetMethodMap, fieldTypeMap);
        while (entityClass != Object.class) {
            Method[] declaredMethods = entityClass.getDeclaredMethods();
            for (Method method : declaredMethods) {
                int modifiers = method.getModifiers();
                // public且不是静态方法
                if (!Modifier.isPublic(modifiers) || Modifier.isStatic(modifiers)) {
                    continue;
                }
                String name = method.getName();
                Class<?> returnType = method.getReturnType();
                int parameterCount = method.getParameterCount();
                int subIdx;
                boolean isGetter = name.startsWith("get");
                if ((isGetter && parameterCount == 0 && returnType != void.class) ||
                        (name.startsWith("set") && parameterCount == 1)) {
                    // get和set方法截取三个字符
                    subIdx = 3;
                } else if (name.startsWith("is") && parameterCount == 0 && returnType != void.class) {
                    // is截取两个字符
                    isGetter = true;
                    subIdx = 2;
                } else {
                    continue;
                }
                String standardFieldName = standardFieldName(name, subIdx);
                if (standardFieldName == null) {
                    continue;
                }
                int hashCode = standardFieldName.hashCode();
                BeanFieldType beanFieldType = fieldTypeMap.computeIfAbsent(standardFieldName, _ -> new BeanFieldType());
                if (isGetter) {
                    // 添加Get方法信息
                    GetMethod getMethod = new GetMethod(name, standardFieldName, returnType.describeConstable().orElseThrow());
                    hashGetMethodMap.computeIfAbsent(hashCode, _ -> new ArrayList<>()).add(getMethod);
                    beanFieldType.setReturnClass(returnType);
                } else {
                    // 添加Set方法信息
                    Class<?> parameterType = method.getParameterTypes()[0];
                    SetMethod setMethod = new SetMethod(name, standardFieldName, parameterType.describeConstable().orElseThrow(),
                            returnType.describeConstable().orElseThrow());
                    hashSetMethodMap.computeIfAbsent(hashCode, _ -> new ArrayList<>()).add(setMethod);
                    beanFieldType.setParamClass(parameterType);
                }
            }
            entityClass = entityClass.getSuperclass();
        }
        return getSetMethod;
    }

    /**
     * 截取后第一个字符小写的不符合规则
     * 第二个字符大写直接使用方法名,否则将第一个字符改为小写
     *
     * @param methodName 方法名
     * @param subIdx     截取index
     * @return 规范字段名
     */
    private static String standardFieldName(String methodName, int subIdx) {
        int length = methodName.length();
        if (length <= subIdx) {
            return null;
        }
        String fieldName = methodName.substring(subIdx, length);
        if (fieldName.length() == 1) {
            return fieldName.toLowerCase();
        }
        char firstChar = fieldName.charAt(0);
        if (Character.isLowerCase(firstChar)) {
            return null;
        }
        char secondChar = fieldName.charAt(1);
        if (Character.isUpperCase(secondChar)) {
            return fieldName;
        }
        return Character.toLowerCase(firstChar) + fieldName.substring(1);
    }

    /**
     * 类型转换,转换基础类型时,先将Object转换成对应包装类,在调用对应方法拆箱
     *
     * @param codeBuilder codeBuilder
     * @param typeDesc    需要的类型
     */
    private static void checkCast(CodeBuilder codeBuilder, ClassDesc typeDesc) {
        // 调用对应的方法进行拆箱
        if (typeDesc == ConstantDescs.CD_int) {
            codeBuilder.checkcast(ConstantDescs.CD_Integer);
            codeBuilder.invokevirtual(ConstantDescs.CD_Integer, "intValue", MethodTypeDesc.of(ConstantDescs.CD_int));
        } else if (typeDesc == ConstantDescs.CD_long) {
            codeBuilder.checkcast(ConstantDescs.CD_Long);
            codeBuilder.invokevirtual(ConstantDescs.CD_Long, "longValue", MethodTypeDesc.of(ConstantDescs.CD_long));
        } else if (typeDesc == ConstantDescs.CD_float) {
            codeBuilder.checkcast(ConstantDescs.CD_Float);
            codeBuilder.invokevirtual(ConstantDescs.CD_Float, "floatValue", MethodTypeDesc.of(ConstantDescs.CD_float));
        } else if (typeDesc == ConstantDescs.CD_double) {
            codeBuilder.checkcast(ConstantDescs.CD_Double);
            codeBuilder.invokevirtual(ConstantDescs.CD_Double, "doubleValue", MethodTypeDesc.of(ConstantDescs.CD_double));
        } else if (typeDesc == ConstantDescs.CD_short) {
            codeBuilder.checkcast(ConstantDescs.CD_Short);
            codeBuilder.invokevirtual(ConstantDescs.CD_Short, "shortValue", MethodTypeDesc.of(ConstantDescs.CD_short));
        } else if (typeDesc == ConstantDescs.CD_byte) {
            codeBuilder.checkcast(ConstantDescs.CD_Byte);
            codeBuilder.invokevirtual(ConstantDescs.CD_Byte, "byteValue", MethodTypeDesc.of(ConstantDescs.CD_byte));
        } else if (typeDesc == ConstantDescs.CD_char) {
            codeBuilder.checkcast(ConstantDescs.CD_Character);
            codeBuilder.invokevirtual(ConstantDescs.CD_Character, "charValue", MethodTypeDesc.of(ConstantDescs.CD_char));
        } else if (typeDesc == ConstantDescs.CD_boolean) {
            codeBuilder.checkcast(ConstantDescs.CD_Boolean);
            codeBuilder.invokevirtual(ConstantDescs.CD_Boolean, "booleanValue", MethodTypeDesc.of(ConstantDescs.CD_boolean));
        } else {
            // 引用类型直接转换
            codeBuilder.checkcast(typeDesc);
        }
    }

    /**
     * 将基本类型进行装箱
     *
     * @param codeBuilder codeBuilder
     * @param typeDesc    需要的类型
     */
    private static void box(CodeBuilder codeBuilder, ClassDesc typeDesc) {
        if (!typeDesc.isPrimitive()) {
            return;
        }
        // 调用对应的valueOf方法进行装箱
        if (typeDesc == ConstantDescs.CD_int) {
            codeBuilder.invokestatic(ConstantDescs.CD_Integer, "valueOf", MethodTypeDesc.of(ConstantDescs.CD_Integer, ConstantDescs.CD_int));
        } else if (typeDesc == ConstantDescs.CD_long) {
            codeBuilder.invokestatic(ConstantDescs.CD_Long, "valueOf", MethodTypeDesc.of(ConstantDescs.CD_Long, ConstantDescs.CD_long));
        } else if (typeDesc == ConstantDescs.CD_float) {
            codeBuilder.invokestatic(ConstantDescs.CD_Float, "valueOf", MethodTypeDesc.of(ConstantDescs.CD_Float, ConstantDescs.CD_float));
        } else if (typeDesc == ConstantDescs.CD_double) {
            codeBuilder.invokestatic(ConstantDescs.CD_Double, "valueOf", MethodTypeDesc.of(ConstantDescs.CD_Double, ConstantDescs.CD_double));
        } else if (typeDesc == ConstantDescs.CD_short) {
            codeBuilder.invokestatic(ConstantDescs.CD_Short, "valueOf", MethodTypeDesc.of(ConstantDescs.CD_Short, ConstantDescs.CD_short));
        } else if (typeDesc == ConstantDescs.CD_byte) {
            codeBuilder.invokestatic(ConstantDescs.CD_Byte, "valueOf", MethodTypeDesc.of(ConstantDescs.CD_Byte, ConstantDescs.CD_byte));
        } else if (typeDesc == ConstantDescs.CD_char) {
            codeBuilder.invokestatic(ConstantDescs.CD_Character, "valueOf", MethodTypeDesc.of(ConstantDescs.CD_Character, ConstantDescs.CD_char));
        } else if (typeDesc == ConstantDescs.CD_boolean) {
            codeBuilder.invokestatic(ConstantDescs.CD_Boolean, "valueOf", MethodTypeDesc.of(ConstantDescs.CD_Boolean, ConstantDescs.CD_boolean));
        } else {
            throw new UnsupportedOperationException("box not supported");
        }
    }

    /**
     * Get和Set方法信息
     *
     * @param hashGetMap   hashCode对应的Get方法
     * @param hashSetMap   hashCode对应Set方法
     * @param fieldTypeMap 字段名对应的类型
     */
    record GetSetMethod(Map<Integer, List<GetMethod>> hashGetMap, Map<Integer, List<SetMethod>> hashSetMap,
                        Map<String, BeanFieldType> fieldTypeMap) {
    }

    /**
     * 字段名
     */
    interface MethodInfo {

        String fieldName();
    }

    /**
     * Set方法信息
     *
     * @param methodName 方法名字
     * @param fieldName  字段名字
     * @param paramType  参数类型
     * @param returnType 返回值类型
     */
    record SetMethod(String methodName, String fieldName, ClassDesc paramType,
                     ClassDesc returnType) implements MethodInfo {
    }

    /**
     * Get方法信息
     *
     * @param methodName 方法名字
     * @param fieldName  字段名字
     * @param returnType 返回值类型
     */
    record GetMethod(String methodName, String fieldName, ClassDesc returnType) implements MethodInfo {
    }

    /**
     * 记录class的软引用,方便从缓存中删除无效的数据
     */
    static class KeySoftReference extends SoftReference<BeanVisitors> {

        /**
         * 缓存key
         */
        final Class<?> key;

        /**
         * @param key      key
         * @param referent BeanVisitors
         */
        public KeySoftReference(Class<?> key, BeanVisitors referent) {
            super(referent, KEY_REFERENCE_QUEUE);
            this.key = key;
        }
    }
}

二、BeanCopier实现Bean属性复制

public interface BeanCopier {

    /**
     * source对象中的属性copy到target
     *
     * @param source 源对象
     * @param target 目标对象
     * @return target
     */
    static <R> R copy(Object source, R target) {
        return copy(source, target, null);
    }

    /**
     * source对象中的属性copy到target,source和target中字段名字相同,类型不同时可以自定义转换规则
     *
     * @param source    源对象
     * @param target    目标对象
     * @param converter 自定义类型转换器
     * @return target
     */
    static <R> R copy(Object source, R target, TypeConverter converter) {
        // 分别创建BeanVisitors
        BeanVisitors sourceVisitors = BeanVisitorFactory.createBeanVisitor(source.getClass());
        BeanVisitors targetVisitors = BeanVisitorFactory.createBeanVisitor(target.getClass());
        // 遍历源对象的字段信息
        for (Map.Entry<String, BeanFieldType> fieldTypeEntry : sourceVisitors.getBeanFieldTypeMap().entrySet()) {
            BeanFieldType getBeanFieldType = fieldTypeEntry.getValue();
            Class<?> returnClass = getBeanFieldType.returnClass;
            if (returnClass == null) {
                continue;
            }
            // 查找目标对象字段信息
            String field = fieldTypeEntry.getKey();
            BeanFieldType setBeanFieldType = targetVisitors.beanFieldType(field);
            if (setBeanFieldType == null || setBeanFieldType.paramClass == null) {
                continue;
            }
            Object fieldValue = null;
            if (returnClass == setBeanFieldType.paramClass) {
                // 类型相同时,直接获取
                fieldValue = sourceVisitors.get(source, field);
            } else if (converter != null) {
                // 类型不同 存在转换器时 进行转换
                fieldValue = sourceVisitors.get(source, field);
                if (fieldValue != null) {
                    fieldValue = converter.convert(fieldValue, setBeanFieldType.paramClass);
                }
            }
            if (fieldValue != null) {
                // 填充
                targetVisitors.put(target, field, fieldValue);
            }
        }
        return target;
    }
}

三、使用示例

public class TestBean {

    public static void main(String[] args) {
        Source source = new Source(1, "test");
        BeanMap beanMap = BeanVisitorFactory.createBeanMap(source);
        System.out.println("beanMap.get(\"i\") = " + beanMap.get("i"));
        beanMap.put("name", "hello");
        System.out.println("beanMap.get(\"name\") = " + beanMap.get("name"));
        System.out.println("beanMap.copyTo(new Target()) = " + beanMap.copyTo(new Target()));
        System.out.println("BeanCopier.copy(source, new Target()) = " + BeanCopier.copy(source, new Target()));
    }

    @Data
    @AllArgsConstructor
    static class Source {

        private int i;

        private String name;
    }

    @Data
    static class Target {

        private Integer i;

        private String name;
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dichotomy_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值