我试图使用GroovyClassLoader和GroovyScriptEngineImpl在Java应用程序中运行Groovy脚本,并希望将Groovy脚本与父应用程序上下文隔离开来。
我的意思是,当我运行我的Groovy脚本时,我不希望它从我的Java应用程序中加载的依赖项继承,以便能够在我的脚本中加载Gson 2.5.5,即使我的Gson 3.4.1应用程序正在使用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之前不更新上下文类加载器,那么脚本运行良好,但它是从父依赖项继承的。
你有什么想法吗?
谢谢:)
UPDATE:经过更多的测试后,编译似乎工作正常,但是编译脚本的评估没有做任何事情。实际上,当我试图编译一个没有所需的所有依赖项的脚本时,即使它们存在于我的Java应用程序类路径中,我也会遇到一个错误。
发布于 2016-07-01 09:30:57
好吧,终于弄明白了,这是个有趣的问题。
我说没有错误,虽然没有打印,但实际上我有一个错误(很明显,因为它不起作用.)。我通过稍微修改我的方法来使用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创建的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);
evaluateMethod.invoke(groovyShellObj, "println(\"test\")");这需要更多的工作,但是脚本将由同一个类加载程序编译和评估,并且问题将得到解决。
我仍然需要使用GroovyScriptEngineImpl来调整这个解决方案以适应我最初的情况,但方法是完全相同的:)
https://stackoverflow.com/questions/38124172
复制相似问题