Java 在groovy中动态加载jar
我有一个groovy脚本createWidget.groovy: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中修改类路径的方法之一): 但这总是失败,导入
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 ();
}
}
}