Java程序运行时编译Scala代码的脚本引擎及类加载问题
解决Java集成Scala脚本引擎的打包及动态依赖问题
一、打包Jar后Scala ScriptEngine获取失败/编译器无法读取Jar的问题
问题原因
- 打包时丢失Scala脚本引擎的SPI配置文件,导致
ScriptEngineManager无法识别Scala引擎 - Fat Jar打包未合并META-INF服务配置,或Scala编译器/类库的类文件被损坏
- 类加载器无法正确加载Scala相关依赖类
解决步骤
- 补全SPI配置文件
在项目src/main/resources/META-INF/services目录下创建javax.script.ScriptEngineFactory文件,写入:
scala.tools.nsc.interpreter.IMain$ScriptEngineFactory
- Fat Jar打包合并服务配置
- Maven Shade插件配置:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.4.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/> </transformers> </configuration> </execution> </executions> </plugin>
- Gradle Shadow插件配置:
shadowJar { mergeServiceFiles() }
- 手动初始化ToolBox时指定类路径
放弃ScriptEngineManager,直接创建实例时明确设置类路径:
import scala.tools.nsc.Settings; import scala.tools.reflect.ToolBox; import scala.tools.reflect.ToolBoxFactory; Settings settings = new Settings(); // 追加基础类路径及Scala依赖Jar settings.classpath().append(System.getProperty("java.class.path")); settings.classpath().append("/path/to/scala-library.jar"); settings.classpath().append("/path/to/scala-compiler.jar"); ToolBox toolBox = new ToolBoxFactory().create(settings);
二、动态添加依赖仅首次eval前生效的问题
问题原因
Scala的ToolBox/IMain在首次执行eval时会固化编译器的类路径,初始化完成后无法修改已加载的类路径配置。
解决步骤
- 添加新依赖后重新创建ToolBox
这是最直接的方案,每次更新依赖后基于新的类路径实例化ToolBox:
import scala.tools.nsc.Settings; import scala.tools.reflect.ToolBox; import scala.tools.reflect.ToolBoxFactory; import java.util.ArrayList; import java.util.List; public class ScalaScriptRunner { private List<String> dynamicJars = new ArrayList<>(); public void addJar(String jarPath) { dynamicJars.add(jarPath); } public Object runScript(String code) { Settings settings = new Settings(); // 加载基础类路径 settings.classpath().append(System.getProperty("java.class.path")); // 追加动态添加的Jar for (String jar : dynamicJars) { settings.classpath().append(jar); } ToolBox toolBox = new ToolBoxFactory().create(settings); return toolBox.eval(toolBox.parse(code)); } }
- 优化方案:缓存不同依赖组合的ToolBox
如果需要频繁切换依赖组,可以为不同的依赖集合创建独立的ToolBox实例并缓存,减少重复初始化的开销。
内容的提问来源于stack exchange,提问作者evgrot
相关产品推荐
相关产品推荐

