我尝试使用GroovyClassLoader
和GroovyScriptEngineImpl
在我的Java应用程序中运行Groovy脚本,并且我希望将Groovy脚本与父应用程序上下文隔离开来。
我的意思是,当我运行我的Groovy脚本时,我不希望它继承自我的Java应用程序中加载的依赖项,以便能够,例如,加载{{ 1}}在我的脚本中,即使我的Java应用程序正在使用Gson 2.5.5
。
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);
之前没有更新我的上下文类加载器,那么脚本工作正常,但它继承自父依赖项。
你有什么想法吗?
谢谢:)
UPDATE :经过一些测试后,看起来编译工作正常,但编译脚本的评估并没有做任何事情。实际上,我在尝试编译一个脚本时遇到错误,即使它们出现在我的Java应用程序类路径中,它们也不需要所有依赖项。
答案 0 :(得分:1)
好的,终于明白了,这是一个有趣的。
我说没有错误,虽然它没有打印,但我实际上有一个(很明显,因为它不起作用......)。我设法通过稍微改变我的方法来使用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
不兼容,因为类是在两个类加载器中定义的(即使它们是代码完全相同的类)。
然后,当尝试转换由Script1
创建的groovyCL
对象时,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);
method.invoke(groovyShellObj, "println(\"test\")");
这需要更多工作,但脚本将由同一个类加载器编译和评估,问题将得到解决。
我仍然需要使用GroovyScriptEngineImpl
来调整此解决方案以适应我的初始情况,但这是完全相同的方法:)