JDK18运行Spring应用报java.lang.IllegalStateException如何解决
异常含义
这个异常是Java 9引入模块化系统(JPMS)后,高版本JDK和旧版第三方库不兼容的典型报错,完整报错链路逻辑如下:
- Spring启动时要对
@Configuration标注的配置类做CGLIB动态代理增强,CGLIB初始化过程中需要通过反射修改ClassLoader.defineClass方法的访问权限,来实现动态类的定义 - JDK 18默认对核心模块的内部API做了严格的访问限制,不允许未命名模块(也就是非模块化的业务代码、旧版第三方Jar)通过反射访问
java.base模块下java.lang包的受保护方法,直接抛出InaccessibleObjectException - 反射初始化失败导致CGLIB的增强器类无法正常加载,最终Spring无法处理配置类,容器启动中断。
核心诱发原因
你提到的JDK版本差异确实是本次问题的核心诱因,具体逻辑:
- Ant脚本里配置的
source=1.8、target=1.8仅能保证javac编译出来的业务代码字节码版本符合JDK 8规范,既不会改变项目依赖的第三方包(Spring、CGLIB)的已有逻辑,也不会自动适配高版本JDK的模块化规则。 - 你的项目是适配JDK 8的遗留应用,依赖的Spring版本大概率是3.x/4.x系列,内置的CGLIB没有做高版本JDK的模块化兼容。不管是构建时用JDK 18连带打包了不兼容的依赖,还是新机器上运行Jar时使用JDK 18作为运行时,只要代码跑在JDK 9及以上的环境,且没有做模块开放配置,就会触发这个报错。
- 旧机器上能正常运行,本质是旧机器的运行时JDK版本为1.8,没有模块化访问限制。
可行解决方案
按改造成本从低到高排序:
- 方案1(零改动最稳妥):全链路使用JDK 8。把执行Ant构建的JDK换成JDK 8,新机器上运行Jar也使用JDK 8作为运行时环境,和原有遗留应用的适配环境保持完全一致,不需要修改任何代码、配置,直接规避所有高版本JDK兼容性问题。
- 方案2(必须用高版本JDK运行时选):添加JVM启动参数开放模块权限。启动Jar时在命令里追加如下参数,放开CGLIB需要的反射访问权限:
java --add-opens java.base/java.lang=ALL-UNNAMED -jar 你的应用包名.jar
如果启动后还出现其他模块的InaccessibleObjectException报错,按照报错提示的模块名、包名,按相同格式追加--add-opens参数即可。这个方案不需要改项目代码,仅调整启动命令。
- 方案3(长期适配选):升级Spring版本。将项目依赖的Spring升级到5.3.x及以上的正式版本,这些版本内置的CGLIB已经完成高版本JDK的模块化适配,不需要额外加启动参数即可在JDK 18上正常运行。注意Spring大版本升级可能存在API变更,需要全量回归业务功能。
内容的提问来源于stack exchange,提问作者user2526586
相关产品推荐
相关产品推荐

