Spring框架对BeanUtils.copyProperties的优化

前言

在高并发环境下,我们难免要进行大量的存库操作,而一般的操作是监听kafka然后将消息转换成实体类,再使用一些orm框架(mybatis-plus,jpa等)进行入库,我们在将消息转换的时候难免要用到反射,今天我们来讲讲Spring框架中对BeanUtils.copyProperties的优化

缓存机制

Spring 的 BeanUtils 类利用了缓存机制来加速属性的访问。具体来说,它缓存了属性描述符(PropertyDescriptor)以减少反射操作的次数。每次复制属性时,它会先检查缓存,以避免重复地使用反射来获取属性描述符

java 复制代码
private static final Map<Class<?>, PropertyDescriptor[]> propertyDescriptorCache =
        new ConcurrentHashMap<>(256);

更高效的反射操作

Spring 使用了一些优化的反射操作来提高性能,例如:

快速方法访问:使用 ReflectionUtils 提供的方法快速访问属性。

避免不必要的反射调用:通过缓存属性描述符和方法访问器,减少反射调用的开销

更灵活的类型转换

Spring 的 BeanUtils.copyProperties 支持类型转换,这意味着如果源对象和目标对象的属性类型不完全匹配,Spring 会尝试进行适当的类型转换。这是通过 Spring 的 ConversionService 实现的,它支持复杂的类型转换逻辑

更好的异常处理

Spring 的 BeanUtils 提供了更好的异常处理机制。它会捕获并处理各种可能的异常情况,并提供详细的异常信息,帮助开发者更快地发现和解决问题

支持嵌套属性复制

Spring 的 BeanUtils.copyProperties 支持嵌套属性的复制,这意味着它可以处理嵌套对象的属性复制,而不仅仅是简单属性。这使得它在处理复杂对象结构时更加灵活和方便。

支持忽略特定属性

Spring 的 BeanUtils.copyProperties 允许开发者指定在复制过程中忽略哪些属性。这对于需要部分属性复制的场景非常有用。

源码分析

以下是 Spring BeanUtils.copyProperties 方法的简化版源码,以展示其主要优化和特性:

java 复制代码
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() + "]");
        }
        actualEditable = editable;
    }
    PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
    List<String> ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null;

    for (PropertyDescriptor targetPd : targetPds) {
        Method writeMethod = targetPd.getWriteMethod();
        if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
            PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
            if (sourcePd != null) {
                Method readMethod = sourcePd.getReadMethod();
                if (readMethod != null &&
                        ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                    try {
                        if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                            readMethod.setAccessible(true);
                        }
                        Object value = readMethod.invoke(source);
                        if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                            writeMethod.setAccessible(true);
                        }
                        writeMethod.invoke(target, value);
                    } catch (Throwable ex) {
                        throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                    }
                }
            }
        }
    }
}
相关推荐
Anastasiozzzz30 分钟前
从有限状态机到智能体图:传统 FSM 与 Agent Graph的演进
java·人工智能·python·ai
wang09077 小时前
自己动手写一个spring之IOC_2
java·后端·spring
来杯@Java7 小时前
学生选课管理系统(基于springboot+vue前后端分离的项目)计算机毕业设计java
java·spring boot·spring·vue·毕业设计·maven·mybatis
豆瓣鸡8 小时前
Spring Cloud笔记
spring·spring cloud
不知名的老吴8 小时前
线程的生命周期之线程“插队“
java·开发语言·python
ANnianStriver8 小时前
PetLumina-02-后端开发与前后端联调
java·ai·sa-token
云烟成雨TD9 小时前
Spring AI 1.x 系列【56】用大模型评判大模型:递归顾问实现自动化评估方案
人工智能·spring·自动化
杨了个杨89829 小时前
Keepalived + Nginx + HAProxy 高可用架构部署实战案例
java·nginx·架构
陈鋆10 小时前
Spring AI Framework(二:模块分析)
spring·ai