• Spring框架对BeanUtils.copyProperties的优化


    前言

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

    缓存机制

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

    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 方法的简化版源码,以展示其主要优化和特性:

    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);
                        }
                    }
                }
            }
        }
    }
    
    
  • 相关阅读:
    Ceres Solver实例分析
    基于JAVA一起组局校园交友平台计算机毕业设计源码+系统+数据库+lw文档+部署
    Linux|qtcreator编译可执行程序双击运行
    海外媒体发稿:8个提升影响力的日韩地区媒体发稿推广策略-华媒舍
    跨境电商:YouTube视频营销必看攻略
    Linux单列模式实现线程池
    驱动:驱动相关概念,内核模块编程,内核消息打印printk函数的使用
    QT的ui设计中改变样式表的用法
    laravel5.1反序列化
    生信教程|替代模型选择
  • 原文地址:https://blog.csdn.net/u013531166/article/details/139737721