首页 > 其他分享 >Spring中使用BeanUtils.copyProperties()导致Hessian/Burlap:ClassNotFoundException

Spring中使用BeanUtils.copyProperties()导致Hessian/Burlap:ClassNotFoundException

时间:2024-11-20 22:40:53浏览次数:1  
标签:ClassNotFoundException Burlap Spring copyProperties 源码 readMethod writeMethod Be

背景

遇到一个问题:

dubbo服务客户端发现提示警告异常

[New I/O worker #4] WARN c.a.c.c.hessian.io.SerializerFactory - Hessian/Burlap: 'XX.XX.XBean' is an unknown class in java.net.URLClassLoader@988246e:

java.lang.ClassNotFoundException: XX.XX.XBean

但是根据代码查到该XBean并没有通过dubbo服务,而是转换为了DTO返回

那么为啥会报这个找不到呢

原因:

BeanUtils 是 org.springframework.beans 包下的类。

BeanUtils.copyProperties,是浅拷贝,引用类型拷贝的是地址。

转换DTO的时候使用的是BeanUtils.copyProperties,浅拷贝,Spring在5.3.0版本之前这个方法的源码中没有泛型的判断,所以通过反射的方法赋值会出现实际的类型与声明的不一致。

比如:List<Child> 拷贝给 List<Parent>,即使范型类型不一致,也能拷贝成功。hession序列化的时候会访问到XBean的引用,由于客户端并不依赖XBean,所以找不到。

源码解析

BeanUtils.copyProperties()源码解析

上面所说的常见的「坑」,翻看Spring的BeanUtils.copyProperties()方法源码,就很容易发现问题出现的原因。

源码核心部分(5.3.8版本):

private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
      @Nullable String... ignoreProperties) throws BeansException {
   // ... 其他
   Class<?> actualEditable = target.getClass();
   //... 其他
   // 获取目标类的所有属性描述(PropertyDescriptor,主要包括属性名称和其相关的读写方法即set、get方法)
   PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
   // ... 其他
   // 遍历所有属性,为每个属性赋值
   for (PropertyDescriptor targetPd : targetPds) {
      // 获取属性的set方法
      Method writeMethod = targetPd.getWriteMethod();
      if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
         // 获取targetBean属性对应sourceBean的属性描述
         PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
         if (sourcePd != null) {
            // sourceBean属性的读方法,即get方法
            Method readMethod = sourcePd.getReadMethod();
            if(readMethod != null) {
                ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
                ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);

                // 判断属性类型是否一致,包括泛型是否一致
                boolean isAssignable =
      (sourceResolvableType.hasUnresolvableGenerics() || targetResolvableType.hasUnresolvableGenerics() ?
            ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) :
            targetResolvableType.isAssignableFrom(sourceResolvableType));

                if (isAssignable) {
                    try {
                          if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                              readMethod.setAccessible(true);
                          }
                         // 通过反射,调用get方法,获取source属性的值
                         Object value = readMethod.invoke(source);
                          if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                     writeMethod.setAccessible(true);
                          }
                          // 通过反射,调用set方法,将source属性的值赋值给target的属性
                          writeMethod.invoke(target, value);
                   }
                   catch (Throwable ex) {
                      throw new FatalBeanException(
                        "Could not copy property '" + targetPd.getName() + "' from source to target", ex);
               }
            }
         }
      }
   }
}

从源码可以看出:

  1. sourceBean和targetBean的属性的拷贝,是通过反射中的Method完成的,所以如果Bean不声明属性的set和get方法,则不能属性间的copy。

  2. Method的invoke方法,只是把sourceBean的get方法获取的值通过targetBean的set方法设置,所以并不涉及深拷贝,只是拷贝属性的引用。

  3. 上面的源码,有一步:属性的泛型是否一致判断。

boolean isAssignable =
      (sourceResolvableType.hasUnresolvableGenerics() || targetResolvableType.hasUnresolvableGenerics() ?
            ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) :
            targetResolvableType.isAssignableFrom(sourceResolvableType));

而在Spring 5.3.0之前并没有这一步,其判断方式:

ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()))
  1. 在5.3.0之前没有泛型的判断,所以通过反射的方法赋值会出现实际的类型与声明的不一致。

本篇文章如有帮助到您,请给「翎野君」点个赞,感谢您的支持。

首发链接:https://www.cnblogs.com/lingyejun/p/18559525

标签:ClassNotFoundException,Burlap,Spring,copyProperties,源码,readMethod,writeMethod,Be
From: https://www.cnblogs.com/lingyejun/p/18559525

相关文章

  • SpringBoot在线网盘管理系统
    感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多的人2025最新毕业设计项目推荐-SSM篇2025最新毕业设计项目推荐-SpringBoot篇2025最新毕业设计项目推荐-小程序、uniapp篇-CSDN博客Java精品毕设实战案例推荐​在......
  • SpringBoot+小程序旅游景区管理系统
    感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多的人2025最新毕业设计项目推荐-SSM篇2025最新毕业设计项目推荐-SpringBoot篇2025最新毕业设计项目推荐-小程序、uniapp篇-CSDN博客Java精品毕设实战案例推荐​小......
  • SpringBoot+小程序的考研分享交流社区
    感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多的人2025最新毕业设计项目推荐-SSM篇2025最新毕业设计项目推荐-SpringBoot篇2025最新毕业设计项目推荐-小程序、uniapp篇-CSDN博客Java精品毕设实战案例推荐​小......
  • SpringBoot项目框架搭建日志相关容易被忽视的细节
    问题项目A、B,都基于SpringBoot技术栈开发。发现其中项目A在本地IDE启动时控制台会打印spring-webmvc里@Controller的mapping映射信息,其中一行如下:2024-11-2021:10:12[main:76835]-[INFO]org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$Mappi......
  • 基于java+springboot的hive的安顺旅游景点数据分析的设计与实现
    课题简介基于Java+SpringBoot和Hive的安顺旅游景点数据分析系统,全力挖掘安顺旅游数据宝藏。后端借SpringBoot构建,SpringDataJPA与MySQL存储关键信息,SpringSecurity筑牢安全防线。Hive存储海量景点数据,如客流、客源地、消费等,HiveQL深度分析挖掘价值。......
  • 基于java+springboot的Hadoop的豆瓣电子图书推荐系统
    课题简介基于Java+SpringBoot和Hadoop的豆瓣电子图书推荐系统,致力于为读者精准推送契合其兴趣的电子图书。后端运用SpringBoot构建稳固架构,SpringDataJPA与MySQL数据库协作存储图书信息、用户数据、评分记录等内容,SpringSecurity保障系统安全,规范用户认......
  • 基于springboot+vue的Java的航空飞机票务预约购票出行服务系统设计与实现(源码+文档+
    课题简介基于SpringBoot+Vue的航空飞机票务预约购票出行服务系统,为旅客提供一站式航空出行解决方案。后端采用SpringBoot构建核心服务,SpringDataJPA与MySQL数据库交互,存储航班信息、用户数据、订单详情等,SpringSecurity确保系统安全,进行严格的用户认证与......
  • Spring Boot项目集成Redisson 原始依赖与 Spring Boot Starter 的流程
    Redisson是一个高性能的JavaRedis客户端,提供了丰富的分布式工具集,如分布式锁、Map、Queue等,帮助开发者简化Redis的操作。在集成Redisson到项目时,开发者通常有两种选择:使用Redisson原始依赖。使用RedissonSpringBootStarter。这篇文章将详细介绍这两种集成......
  • 【开源免费】基于SpringBoot+Vue.JS在线文档管理系统(JAVA毕业设计)
    本文项目编号T038,文末自助获取源码\color{red}{T038,文末自助获取源码}......
  • 【开源免费】基于SpringBoot+Vue.JS网上订餐系统(JAVA毕业设计)
    本文项目编号T039,文末自助获取源码\color{red}{T039,文末自助获取源码}......