使用BeanUtils返回前端为空值?
案例代码:
public static <T> T beanConvert(Object from, Class<T> clazz, Converter converter) {if (from == null) return null;try {T t = clazz.newInstance();BeanCopier beanCopier = BeanCopier.create(from.getClass(), clazz, converter != null);beanCopier.copy(from, t, converter);return t;} catch (Exception e) {// 添加详细的异常日志System.err.println("BeanConvert failed: " + from.getClass().getName() + " -> " + clazz.getName());System.err.println("Exception: " + e.getMessage());e.printStackTrace();return null;}}
出现错误:
BeanConvert failed: com.ydc.business.admin.post.entity.YPostCategory -> com.ydc.business.admin.post.web.response.YPostCategoryListResponse
Exception: java.lang.reflect.InaccessibleObjectException-->Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @544fe44c
org.springframework.cglib.core.CodeGenerationException: java.lang.reflect.InaccessibleObjectException-->Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @544fe44c
错误分解:
1. BeanConvert failed: ...
- 含义:程序尝试将一个对象(
YPostCategory
)转换为另一个对象(YPostCategoryListResponse
),比如使用了BeanUtils.copyProperties()
、MapStruct、Dozer 或 Spring 的BeanWrapper
等工具。 - 这类转换在背后可能依赖 动态生成类(例如 CGLIB 动态代理或 ASM 字节码生成)。
2. Exception: java.lang.reflect.InaccessibleObjectException
- 核心异常:Java 反射试图访问一个被模块系统保护的类或方法,但没有权限。
- 具体是:尝试通过反射访问
ClassLoader.defineClass(...)
方法。
3. Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(...) accessible
- CGLIB(或 ASM)等字节码生成库在运行时需要调用
ClassLoader.defineClass()
来定义新生成的类。 - 该方法是
protected
的,正常情况下可以通过反射setAccessible(true)
来访问。 - 但在 Java 9 及以上版本引入了模块系统(JPMS) 后,如果模块没有显式
opens
,即使使用反射也无法访问内部 API。
4. module java.base does not "opens java.lang" to unnamed module @...
java.base
是 Java 的核心模块,包含java.lang
包。- 默认情况下,
java.base
模块 不会向未命名模块(unnamed module)开放(opens)java.lang
包。 - 你的应用运行在一个“未命名模块”中(典型的传统 classpath 应用),因此无法反射访问
java.lang.ClassLoader
的protected
方法。
5. org.springframework.cglib.core.CodeGenerationException
- Spring 使用的 CGLIB 库在生成动态类时失败,根本原因是上面的
InaccessibleObjectException
。
解决方法:
方案1: 添加JVM启动参数(最简单有效)
在你的IDE运行配置中添加JVM参数:
IntelliJ IDEA:
- 点击运行配置下拉菜单
- 选择 "Edit Configurations..."
- 找到你的Spring Boot运行配置
- 在 "VM options" 字段中添加:
--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED
其他可选方案:
降级 JDK 版本(不推荐)
- 使用 Java 8,它没有模块系统限制。
- 但长期来看,应适配新版本 JDK。
避免使用 CGLIB(有时不可行)
- 改用接口 + JDK 动态代理(要求被代理类实现接口)。
- 例如:Spring AOP 中尽量使用接口。
升级依赖库版本
- 确保你使用的 Spring、CGLIB、ASM 等库是较新版本,它们对 Java 16+ 的模块系统有更好的兼容性。