【问题标题】:GroovyClassLoader - Isolate from parent class loaderGroovyClassLoader - 与父类加载器隔离
【发布时间】:2016-06-30 13:11:21
【问题描述】:

我正在尝试使用 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 应用程序类路径中。

【问题讨论】:

    标签: java groovy classloader


    【解决方案1】:

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

    我说没有错误,虽然它没有打印出来,但我实际上有一个(很明显,因为它没有工作......)。我设法通过稍微改变我使用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 使这个解决方案适应我的初始情况,但它是完全相同的方法:)

    【讨论】:

      猜你喜欢
      • 2012-07-13
      • 1970-01-01
      • 1970-01-01
      • 2016-10-08
      • 2017-10-31
      • 1970-01-01
      • 1970-01-01
      • 2016-04-24
      • 1970-01-01
      相关资源
      最近更新 更多