【问题标题】:Dynamically load jar in groovy在 groovy 中动态加载 jar
【发布时间】:2013-04-25 16:14:21
【问题描述】:

我有一个 groovy 脚本 createWidget.groovy:

 import com.example.widget

 Widget w = new Widget()

当我这样运行这个脚本时,它运行得很好:

$ groovy -cp /path/to/widget.jar createWidget.groovy

但是,我想在脚本中硬编码classpath,这样用户就不需要知道它在哪里,所以我修改createWidget.groovy如下(这是groovy中修改classpath的方法之一):

this.getClass().classLoader.rootLoader.addURL(new File("/path/to/widget.jar").toURL())

import com.example.widget

Widget w = new Widget()

但这总是失败并在导入时出现运行时错误:unable to resolve class com.example.widget

这看起来确实不正统,我认为您不能在导入之前弄乱 rootLoader 还是其他什么?

【问题讨论】:

  • 写一个批处理文件或bash脚本来运行带有cp参数的groovy可能更容易?
  • 您可以通过删除它并尝试def w = Class.forName( 'com.example.Widget' ).newInstance() 代替构造函数来测试您的导入理论...

标签: java groovy classpath


【解决方案1】:
// Use the groovy script's classLoader to add the jar file at runtime.
this.class.classLoader.rootLoader.addURL(new URL("/path/to/widget.jar"));

// Note: if widget.jar file is located in your local machine, use following:
// def localFile = new File("/path/tolocal/widget.jar");
// this.class.classLoader.rootLoader.addURL(localFile.toURI().toURL());

// Then, use Class.forName to load the class.
def cls = Class.forName("com.example.widget").newInstance();

【讨论】:

  • 不幸的是,rootLoader 不包含 addURL 方法(classLoader 包含)URL 格式不正确 - 应该是“file:///path/to/widget.jar”Class.forName 返回异常 java。 lang.ClassNotFoundException
【解决方案2】:

如果我理解这个问题,那么您要求的是一个可以交付给用户的独立单元。

这可以在 JVM 上使用“jar”的基础知识。

例如,this project 玩一个名为 War-O 的简单游戏。用户可以使用以下命令运行它:

java -jar warO.jar [args]

该技术包括:(a) 将 Groovy 编译为类文件和 (b) 在 jar 中包含所有必要的 jar(包括 groovy-all)。此外,jar 必须在清单中指定“main”入口点(这将是脚本的修改版本)。

War-O 项目使用 Gradle(参见build file here),但即使使用 Ant、Maven 等,原则也适用。以 Gradle 为例:

jar.archiveName 'warO.jar'
jar.manifest {
    attributes 'Main-Class' : 'net.codetojoy.waro.Main'
    attributes 'Class-Path' : 'jars/groovy-all-1.6.4.jar jars/guava-collections-r03.jar jars/guava-base-r03.jar'
}

【讨论】:

    【解决方案3】:

    另一种方法是写下 java 代码以从 jar 文件加载类,然后按照常规修改该代码。

    import java.net.URL;
    import java.net.URLClassLoader;
    import java.lang.reflect.Method;
    public class JarFileLoader 
    {
        public static void main (def args)
        {
            try
            {
                URLClassLoader cl = new URLClassLoader (new URL("jar:file:///path/to/widget.jar!/"));
    
                System.out.println ("Attempting...");
    
                Class beanClass = cl.loadClass ("com.example.widget.WidgetClass");
                Object dog = beanClass.newInstance();
    
                Method method = beanClass.getDeclaredMethod ("setBean", String.class);
                method.invoke (dog, "Who let the dog out");
    
                method = beanClass.getDeclaredMethod("getBean", null);            
                Object retObj = method.invoke (dog, null);
    
                String retVal = (String)retObj;
    
                System.out.println(retVal);
                System.out.println("Success!");
            }
            catch (Exception ex)
            {
                System.out.println ("Failed.");
                ex.printStackTrace ();
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      Groovy 是一种编译语言,类名在编译时必须是可解析的。因此在运行时添加 Jar 是不够的。

      (import语句也是错误的,你必须追加.*.Widget。但这并不能解决更深层次的问题。)

      【讨论】:

      • Groovy 不是这样。 Groovy 是动态的,通常甚至不知道您所指的类——它使用“Duck Typing”在运行时尝试一个方法并查看它是否有效。 Java 至少需要在编译时可用的接口或基类,但在编译时不需要最终类(这就是为什么您可以将自己的对象传递到集合中,即使在编译集合时您的对象不可用,并且为什么 Tomcat 不用重新编译就能处理你的战争)
      猜你喜欢
      • 2012-11-10
      • 1970-01-01
      • 2016-05-04
      • 1970-01-01
      • 1970-01-01
      • 2015-05-11
      • 2014-12-24
      • 2011-06-07
      • 1970-01-01
      相关资源
      最近更新 更多