You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将父Bean属性复制到子Bean?Apache Commons BeanUtils失效原因

问题原因分析

这个坑我踩过!Apache Commons BeanUtils的copyProperties方法默认是基于Getter/Setter方法来识别和复制属性的,而不是直接操作对象的字段。

看你的代码:Person类里的name字段是默认包私有访问权限,而且没有定义对应的getName()和setName()方法。BeanUtils在处理时,会通过JavaBean的属性描述符(PropertyDescriptor)来识别属性,没有Getter/Setter的字段会被它忽略,自然就复制不了name值到子类Child里,所以你的断言会失败。

解决方案(无需添加Getter/Setter)

既然你不想加Getter/Setter,也不想手动逐个复制字段,这里给你几个可行的方案:

方案1:用Apache Commons Lang的FieldUtils直接操作字段

如果你已经在使用Apache的工具库,可以用FieldUtils来绕过Getter/Setter,直接读写字段。写一个简单的工具方法就行:

private static void copyInheritedFields(Object target, Object source) throws IllegalAccessException {
    Class<?> currentClass = source.getClass();
    // 遍历源对象的类及其所有父类(直到Object类)
    while (currentClass != null && currentClass != Object.class) {
        Field[] fields = currentClass.getDeclaredFields();
        for (Field field : fields) {
            // 读取源字段的值,写入目标对象的同名字段,true表示允许访问非public字段
            FieldUtils.writeField(target, field.getName(), FieldUtils.readField(source, field.getName(), true), true);
        }
        currentClass = currentClass.getSuperclass();
    }
}

把测试里的BeanUtils.copyProperties(c, p)换成这个方法,就能成功复制name字段了。

方案2:用ModelMapper(配置优先访问字段)

ModelMapper是一个强大的对象映射库,可以通过配置让它直接访问字段,不需要Getter/Setter。首先需要引入依赖(Maven为例):

<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>3.1.1</version>
</dependency>

然后配置并使用:

@Test
public void test() throws Exception {
    Person p = new Person();
    p.name = "Dummy";
    Child c = new Child();
    
    ModelMapper modelMapper = new ModelMapper();
    modelMapper.getConfiguration()
        .setFieldMatchingEnabled(true) // 启用字段匹配
        .setFieldAccessLevel(AccessLevel.PRIVATE); // 允许访问所有权限的字段
    modelMapper.map(p, c);
    
    assertEquals(p.name, c.name); // 现在会成功
}

方案3:自己写反射工具类(无额外依赖)

如果不想引入新的依赖,自己用Java反射实现也很简单:

public static void copyAllFields(Object target, Object source) throws IllegalAccessException {
    Class<?> sourceClass = source.getClass();
    Class<?> targetClass = target.getClass();
    
    while (sourceClass != null && sourceClass != Object.class) {
        Field[] fields = sourceClass.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Field targetField = targetClass.getDeclaredField(field.getName());
                targetField.setAccessible(true);
                targetField.set(target, field.get(source));
            } catch (NoSuchFieldException e) {
                // 目标类没有该字段则跳过(子类继承父类的情况不会触发这个)
                continue;
            }
        }
        sourceClass = sourceClass.getSuperclass();
    }
}

这个方法会遍历源对象所有类(包括父类)的字段,强制设置可访问后复制到目标对象的同名字段。

额外说明

你的Child类里的birthday字段是private,这不影响父类字段的复制,问题的核心始终是BeanUtils依赖Getter/Setter的机制。上面的方案都能绕过这个限制,满足你无需手动复制、不添加Getter/Setter的需求。

内容的提问来源于stack exchange,提问作者membersound

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:31:41