Springframework的BeanUtils.copyProperties原理源码浅层解析

本文介绍了Spring的BeanUtils.copyProperties方法的工作原理,涉及JavaBean的内省技术,探讨了内省缓存与ClassLoader的关系,以及Spring如何处理类加载器关闭时的内存回收问题。同时,文中给出了作者对源码的浅层分析,讨论了忽略属性的处理和反射的使用。

新手原创,不喜轻喷,欢迎交流。       

       最近项目交付了,想着项目中遇到的一个问题,在传递返回的包装类给app端的时候有的字段位是null,如果这个字段app端需要其中的值,如果获取的值是null的时候会导致app的闪退,为了解决这个问题,我不得不把所有需要传递给app端且有可能为空的字段一个一个赋“”或者0等,为了简便,我把返回的包装类的数据类型只保留String和Integer.写出了以下方法:

@SuppressWarnings("unused")
public class ParameterInit {
public static <E> void inChange2(Object o) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException, NoSuchMethodException, SecurityException {
Class<? extends Object> c = o.getClass();                //获取要返回的类的class
Method[] methods = c.getMethods();                     //获取其中的方法(自定义的方法都是public)
for (Method m : methods) {
String methodName = m.getName();                //获取方法名
if(methodName.startsWith("set")) {                    //判断方法前缀(整个操作只有set/get有关)
String getMethod = methodName.substring(3);        //截取除set外的其余部分
Method method = c.getMethod("get"+getMethod );       //拼成get就变成了这个属性的get方法
Object invoke = method.invoke(o);             //因为get方法是没有参数的,所以直接传obj进去运行方法

if( invoke == null ) {                //判断当前方法的返回值

Type[] genericParameterTypes = m.getGenericParameterTypes();    //获取set方法的参数类型

if( genericParameterTypes[0] == String.class) {            //匹配参数类型
m.invoke(o, "");                        //如果是String类型就赋值“”
}else if( genericParameterTypes[0] == Integer.class) {    
m.invoke(o, 0);                        //如果是Integer类型就赋值0
}
}
}
}
System.out.println(o.toString());   //测试用,打印看是否所有字段都不为null
}

}

注:以上操作的参数Object是经过springframework的BeanUtils.copyProperties赋值过后的类

做完这些之后高兴的和领导说了一下我做了一个工具类,然后领导说就这个问题把copyProperties研究一下,做一个类似的吧

下面就说说对copyProperties的研究心得,做为一个新手,写的不好大家不要喷,谢谢

先说一下spring源在下载,用git下的方式是

git clone https://github.com/spring-projects/spring-framework.git

spring源码是用gradle写的,所以需要用gradle编译才能放到eclipse中,gradle安装这个大家百度吧

然后用gradle编译的命令是 gradle cleanidea eclipse

以下是copyProperties的源码

        public static void copyProperties(Object source, Object target) throws BeansException {
copyProperties(source, target, null, (String[]) null);

}

可以看到,这里面只是一个方法的调用

private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties)
throws BeansException {
Assert.notNull(source, "Source must not be null");

Assert.notNull(target, "Target must not be null");

Class<?> actualEditable = target.getClass();
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
"] not assignable to Editable class [" + editable.getName() + "]");
}
### BeanUtils.copyProperties 方法的用法 `BeanUtils.copyProperties()` 是 Spring Framework 提供的一个便捷方法,用于将一个 Java 对象的属性值复制到另一个对象中。该方法通过反射机制自动识别并匹配源对象和目标对象之间的相同属性名,并进行赋值。 ```java public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansException ``` - `source`: 源对象,从中读取属性值。 - `target`: 目标对象,将接收来自源对象的属性值。 - `ignoreProperties`: 可选参数,指定在复制过程中忽略的属性名称数组。 例如,假设有两个类 `User` 和 `UserDTO`,它们具有相同的字段名如 `name`、`age` 等,则可以使用如下方式复制属性: ```java User user = new User("Alice", 30); UserDTO userDTO = new UserDTO(); BeanUtils.copyProperties(userDTO, user); // 将user中的属性复制到userDTO中 ``` 需要注意的是,该方法是单向复制,即从 `source` 到 `target`,并且不会自动处理嵌套或复杂类型的属性[^1]。 ### 从 Spring 5.3.x 开始的变化 自 Spring Framework 5.3 起,`BeanUtils.copyProperties()` 方法引入了对泛型类型信息的支持。这是通过内部使用的 `ResolvableType` 类来实现的,它能够更准确地解析和匹配带有泛型参数的属性类型。 这意味着当复制包含泛型集合(如 `List<String>`)或其他复杂泛型结构的属性时,该方法现在能更好地保持类型一致性,从而减少运行时错误的可能性[^2]。 ### 注意事项 1. **可访问性**:确保源对象的 getter 方法和目标对象的 setter 方法都是公开的(public),否则可能会抛出异常或者无法正确复制某些属性。 2. **性能考量**:由于此方法依赖于反射机制,因此其性能通常不如直接调用 getter/setter 方法。对于高性能要求的应用场景,建议谨慎使用或考虑其他替代方案[^4]。 3. **忽略特定属性**:可以通过传递 `ignoreProperties` 参数来排除不需要复制的属性。这对于避免覆盖只读字段或敏感数据非常有用。 4. **类型转换**:如果源属性与目标属性的数据类型不一致,Spring 会尝试进行自动转换。然而,并非所有类型都能被成功转换;在这种情况下,应该手动处理以防止潜在的问题[^4]。 5. **空值处理**:默认情况下,如果源属性为 null,则目标属性也会被设置为 null。根据业务需求,有时可能需要保留原有值而不是用 null 替换。此时需自定义逻辑来控制行为。 6. **不可变对象**:若目标对象中有任何不可变属性(例如 final 字段),则这些属性不能被修改,这会导致调用失败并抛出异常。 7. **安全性**:不要盲目信任输入数据,在安全敏感环境中应验证输入对象的内容,以防注入攻击等安全风险。 8. **版本兼容性**:考虑到不同版本间存在差异,尤其是关于泛型支持方面,请确认所使用的 Spring 版本是否满足项目需求。 9. **异常处理**:该方法声明抛出了 `BeansException`,所以在实际应用中应当妥善捕获并处理此类异常,以免程序因意外情况而中断。 10. **深度拷贝 vs 浅拷贝**:`BeanUtils.copyProperties()` 实现的是浅拷贝。也就是说,如果属性是指针类型(比如另一个对象实例),那么只会复制引用地址而非创建新的副本。要执行深拷贝,则必须自行实现相关逻辑或者借助第三方库如 Dozer、ModelMapper 等。 11. **日志记录**:在调试阶段开启详细的日志输出可以帮助追踪哪些属性被成功复制以及是否存在未能匹配的情况,这对排查问题很有帮助。 12. **单元测试**:为了保证功能正常工作,编写针对各种边缘情况(如空对象、非法参数等)的单元测试是非常重要的一步。 ---
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值