如何将父Bean属性复制到子Bean?Apache Commons BeanUtils失效原因
这个坑我踩过!Apache Commons BeanUtils的copyProperties方法默认是基于Getter/Setter方法来识别和复制属性的,而不是直接操作对象的字段。
看你的代码:Person类里的name字段是默认包私有访问权限,而且没有定义对应的getName()和setName()方法。BeanUtils在处理时,会通过JavaBean的属性描述符(PropertyDescriptor)来识别属性,没有Getter/Setter的字段会被它忽略,自然就复制不了name值到子类Child里,所以你的断言会失败。
既然你不想加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

