Java GroovyClassLoader-与父类装入器隔离

Java GroovyClassLoader-与父类装入器隔离,java,groovy,classloader,Java,Groovy,Classloader,我试图使用GroovyClassLoader和GroovyScriptEngineImpl在我的Java应用程序中运行Groovy脚本,我想将Groovy脚本与父应用程序上下文隔离开来 我的意思是,当我运行Groovy脚本时,我不希望它继承Java应用程序中加载的依赖项,以便能够在脚本中加载Gson 2.5.5,即使Java应用程序正在使用Gson 3.4.1 // Replacing the context class loader with the system one ClassLoade

我试图使用
GroovyClassLoader
GroovyScriptEngineImpl
在我的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
而不是
GroovyScriptEngineImpl
GroovyClassLoader
略微更改执行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
(或者我之前使用的
groovyscript引擎eimpl
等的机制)将遇到
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
使此解决方案适应我的初始情况,但它是完全相同的方法:)