java - GroovyClassLoader - 与父类加载器隔离

标签 java groovy classloader

我正在尝试使用 GroovyClassLoaderGroovyScriptEngineImpl 在我的 Java 应用程序中运行 Groovy 脚本,并且我想将 Groovy 脚本与父应用程序上下文隔离开来。

我的意思是,当我运行我的 Groovy 脚本时,我不希望它继承我的 Java 应用程序中加载的依赖项,以便能够加载 Gson 2.5。 5 在我的脚本中,即使我的 Java 应用程序使用 Gson 3.4.1

// Replacing the context class loader with the system one
ClassLoader initialCL = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader().getParent());

// Creating my GroovyClassLoader and GroovyScriptEngine
GroovyClassLoader groovyCL = new GroovyClassLoader();
GroovyScriptEngineImpl scriptEngine = new GroovyScriptEngineImpl(groovyCL);

// Compiling and running my Groovy script
CompiledScript compiledScript = scriptEngine.compile("println \"hello\"");
compiledScript.eval();

// Going back to my initial classloader
Thread.currentThread().setContextClassLoader(initialCL);

这样,隔离确实有效,但脚本的内容根本没有执行(例如,甚至在控制台中打印一行),而且我在任何地方都没有出错。

如果我在创建新的 GroovyClassLoader 之前不更新我的上下文类加载器,则脚本工作正常,但它是从父依赖项继承的。

你有什么想法吗?

谢谢:)

更新:经过更多测试后,似乎编译工作正常,但编译脚本的评估没有做任何事情。事实上,我在尝试编译一个不具备所有所需依赖项的脚本时遇到错误,即使它们存在于我的 Java 应用程序类路径中也是如此。

最佳答案

好吧,终于想通了,这很有趣。

我说过没有错误,虽然它没有打印出来,但我实际上有一个(很明显,因为它没有工作......)。我设法通过稍微更改我的方法来使用 GroovyShell 而不是我的 GroovyScriptEngineImplGroovyClassLoader 执行 Groovy 脚本来得到错误:

Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader().getParent());
GroovyClassLoader groovyCL = new GroovyClassLoader();

new GroovyShell(groovyCL).evaluate("println(\"test\")");

这是我最终得到的错误,出于某种原因,在我之前的执行过程中隐藏了这个错误,没有使用 GroovyShell:

groovy.lang.GroovyRuntimeException: Failed to create Script instance for class: class Script1. Reason: java.lang.ClassCastException: Script1 cannot be cast to groovy.lang.GroovyObject

那么问题是什么?

实际上,用于编译脚本的类加载器和用于评估编译脚本的类加载器是不同的:groovyCL 用于编译脚本,而“当前线程” "类加载器用于创建 GroovyShell 对象并评估脚本。

这意味着来自两个类加载器的 groovy.lang.GroovyObject 不兼容,因为类是在两个类加载器中定义的(即使它们在代码方面是完全相同的类)。

然后,当尝试转换由 groovyCL 创建的 Script1 对象时,GroovyShell(或我之前使用的机制 GroovyScriptEngineImpl 等...)将遇到 ClassCastException

这整件事会导致一些有趣的错误,例如:

java.lang.ClassCastException: groovy.lang.GroovyShell cannot be cast to groovy.lang.GroovyShell

解决方案

因此,您要做的是创建您的 GroovyClassLoader 实例,并使用该类加载器,使用反射创建工作流的所有对象:

GroovyClassLoader groovyCL = new GroovyClassLoader();

Class groovyShellClazz = groovyCL.loadClass(GroovyShell.class.getName());
Object groovyShellObj = groovyShellClazz.newInstance();
Method evaluateMethod = groovyShellClazz.getMethod("evaluate", String.class);
evaluateMethod.invoke(groovyShellObj, "println(\"test\")");

这需要更多的工作,但脚本将由同一个类加载器编译和评估,问题将得到解决。

我仍然需要使用 GroovyScriptEngineImpl 使这个解决方案适应我的初始情况,但它是完全相同的方法:)

关于java - GroovyClassLoader - 与父类加载器隔离,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38124172/

相关文章:

grails - 使用Groovy检查对象是否在集合中

java - 加载程序约束违规 : loader (instance of x) previously initiated loading for a different type with name y

java - 当同一个类存在于同一服务器上的不同应用程序中时,类加载如何工作?

arrays - 如何从 groovy 对象创建一个 JSON 数组作为字符串?

java - 在任务创建期间访问项目扩展?

java - 类加载器和服务加载器。使用哪个类加载器?

java - 我试图直接将值作为参数发送到我的数组,但它不起作用

java - 设置队列 key

c# - C#应用程序如何调用java应用程序

java - 到处覆盖 onStop?