Java 在groovy中动态加载jar

Java 在groovy中动态加载jar,java,groovy,classpath,Java,Groovy,Classpath,我有一个groovy脚本createWidget.groovy: import com.example.widget Widget w = new Widget() 当我这样运行脚本时,它运行得非常好: $ groovy -cp /path/to/widget.jar createWidget.groovy 但是,我想在脚本中硬编码类路径,这样用户就不需要知道它在哪里,所以我修改了createWidget.groovy如下(这是groovy中修改类路径的方法之一): 但这总是失败,导入

我有一个groovy脚本createWidget.groovy:

 import com.example.widget

 Widget w = new Widget()
当我这样运行脚本时,它运行得非常好:

$ groovy -cp /path/to/widget.jar createWidget.groovy
但是,我想在脚本中硬编码类路径,这样用户就不需要知道它在哪里,所以我修改了createWidget.groovy如下(这是groovy中修改类路径的方法之一):

但这总是失败,导入时出现运行时错误:
无法解析class com.example.widget


这看起来确实很不正常,我想你不能在导入之前弄乱rootLoader,或者是别的什么东西?

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


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

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

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

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

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

War-O项目使用Gradle(请参阅),但即使使用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'
}

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

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 ();
        }
    }
}

只编写一个批处理文件或bash脚本来运行带有cp参数的groovy可能会更容易一些,您可以通过删除它并尝试用
def w=Class.forName('com.example.Widget').newInstance()
代替构造函数来测试导入理论……groovy不是这样的。Groovy是动态的,通常甚至不知道您所引用的类——它使用“Duck-Typing”在运行时尝试一个方法,看看它是否有效。Java至少需要编译时可用的接口或基类,但不需要编译时的最终类(这就是为什么即使在编译集合时对象不可用,也可以将自己的对象传递到集合中,以及Tomcat可以在不重新编译的情况下处理war的原因)不幸的是,不适用于我rootLoader不包含addURL方法(classLoader包含)URL格式不正确-应为“file:///path/to/widget.jar“Class.forName返回异常java.lang.ClassNotFoundException
// 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();
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 ();
        }
    }
}