Spring Boot调用Java编译器API导入自定义类报ClassNotFoundException/NoClassDefFoundError如何解决
问题原因
你遇到的报错分为两个阶段:
- 初始的
ClassNotFoundException是编译阶段错误:JavaCompiler找不到自定义的Utility类,因为你没有给编译器指定依赖类的类路径 - 后续的
NoClassDefFoundError是运行阶段错误:你只给编译器加了类路径解决了编译问题,但是加载动态生成类的URLClassLoader只包含了动态类的存放目录,没有包含项目自身的类路径,无法加载TestClass依赖的Utility类
解决方案
1. 修正编译阶段的类路径参数
你之前的编译参数写法有语法错误,-cp需要作为单独的参数传入,类路径作为下一个参数,正确写法如下:
// 获取Utility所在的类路径 String projectClassPath = Utility.class.getProtectionDomain().getCodeSource().getLocation().getPath(); // 编译参数按顺序传:-cp 类路径 源文件路径 compiler.run(null, null, null, "-cp", projectClassPath, sourceFile.getPath() );
2. 修正运行阶段的类加载器
创建URLClassLoader的时候,需要把项目自身的类路径也加入加载路径,更简单的方案是直接把当前应用的类加载器设为父加载器,直接复用应用已有的类加载逻辑,无需手动添加所有依赖路径:
// 把动态类存放路径和项目类路径都加入URLClassLoader,同时设置父加载器为当前应用的类加载器 URLClassLoader classLoader = URLClassLoader.newInstance( new URL[]{root.toURI().toURL(), new File(projectClassPath).toURI().toURL()}, this.getClass().getClassLoader() // 用当前应用的类加载器作为父加载器,兼容Spring Boot所有依赖加载 ); cls = Class.forName("TestClass", true, classLoader);
3. 附加优化建议
- 不要把动态生成的类存放在
/src/main/resources目录下,这个是源码目录,Spring Boot打包成可执行jar后该路径不存在,建议存放在系统临时目录:File root = new File(System.getProperty("java.io.tmpdir"), "dynamic_classes"); - 如果你的Spring Boot应用会打包成fat jar运行,优先用父加载器的方案,无需手动处理jar内路径的解析问题
内容的提问来源于stack exchange,提问作者Nilanka Manoj
相关产品推荐
相关产品推荐

