【发布时间】:2012-06-16 01:57:17
【问题描述】:
从概念上讲,当我加载一个新 Jar 时我在做什么? URLClassloader 是唯一的选择吗?我应该如何形成这些 URL 以指向包含更多 jar 的子目录。
如果有人觉得超级慷慨,那么一些执行以下操作的演示代码将非常有帮助(假设“jars/A.jar”包含我们要实例化的“myClass”):
- 从子目录加载一些 jar
- 从中返回一个特定的类
- 实例化该类
【问题讨论】:
标签: java classloader
从概念上讲,当我加载一个新 Jar 时我在做什么? URLClassloader 是唯一的选择吗?我应该如何形成这些 URL 以指向包含更多 jar 的子目录。
如果有人觉得超级慷慨,那么一些执行以下操作的演示代码将非常有帮助(假设“jars/A.jar”包含我们要实例化的“myClass”):
【问题讨论】:
标签: java classloader
从概念上讲,当我加载一个新 Jar 时我在做什么?
从概念上讲,您不是在加载新的 JAR。相反,您正在定义一个类加载器,它将按需从 JAR 文件中加载代码和其他资源。
URLClassloader 是唯一的选择吗?
理论上,您可以实现自己的 ClassLoader 子类,但这对于您想要做的事情来说不是必需的。
我应该如何形成这些 URL 以指向包含更多 jar 的子目录。
我认为这是您问题的关键。 URLClassLoader 构造函数解释 URL[] 参数如下:
“任何以 '/' 结尾的 URL 都假定指向一个目录。否则,假定 URL 指向一个 JAR 文件,该文件将根据需要下载和打开。”
在上面引用文本的第一种情况下,目录被假定为包含要加载的资源的树的根。
但我猜您正在尝试设置一个类加载器,该类加载器将从给定目录中的所有 JAR 文件加载。为此,您需要:
File 对象列表。URL 实例。File,使用 File.toURL() 创建一个 URL 并添加到数组中。 (使用 File.toURL() 意味着您将获得适用于您的平台的 JAR 文件的 kosher "file:" URL。)URL 数组创建URLClassLoader。【讨论】:
非常短的版本:当您加载新的 JAR 时,您为 Java 提供了一个寻找类的新位置。
为了更详细的解释,维基百科有一篇与此相关的文章:http://en.wikipedia.org/wiki/Java_Classloader
另外,不妨试试:http://onjava.com/pub/a/onjava/2005/01/26/classloading.html
URLClassLoader 不是您唯一的选择;查看http://download-llnw.oracle.com/javase/tutorial/deployment/jar/apiindex.html
Re:如何形成 URL,来自 Java 文档 (http://download-llnw.oracle.com/javase/7/docs/api/java/net/URLClassLoader.html):
此类加载器用于从引用 JAR 文件和目录的 URL 的搜索路径加载类和资源。假设任何以“/”结尾的 URL 都指向一个目录。否则,假定 URL 引用将根据需要打开的 JAR 文件。
改编自How should I load Jars dynamically at runtime?:
URLClassLoader child = new URLClassLoader (new URL("jars/A.jar"), this.getClass().getClassLoader());
Class classToLoad = Class.forName ("com.myClass", true, child);
Method method = classToLoad.getDeclaredMethod ("myMethod");
Object instance = classToLoad.newInstance ();
Object result = method.invoke (instance);
此外,上面链接的答案列出了 URLClassLoader 的更多替代方案(他们提到了 OSGi、JCL 和其他一些,我没有任何经验,或者我会在这里说些什么)。
HTH
【讨论】:
如果知道 jar 列表,那么一个可能更好/更简单的解决方案就是向父 jar 的清单添加一个 Class-Path 条目。这样您就可以完全避免使用自定义类加载器。
Class-Path: jars/A.jar
显然,在构建时不知道依赖 jar 的情况下,这将不起作用。
【讨论】:
简单的方法是在启动程序之前确保依赖的 JAR 在类路径中。
如果你想从一个子目录加载,你需要确定JAR的路径给URLClassLoader。你可以使用这样的东西来获取你的程序的路径:
new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath())
这有点像 hack,但我不知道有任何官方方法可以获取 Java 程序的路径。
如果您想动态加载 JAR 以便可以像在类路径中指定它一样使用它,请查看 thread。它绝对属于不受支持的 hack 领域,但它也很简单。
我使用的修改版:
import java.io.File;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
/**
* Hack to modify classpath at runtime. Can be used to load JARs as if they were
* loaded from the start.
*
* Warning: May break if URLClassLoader changes
*
* @author Antony Miguel
*/
public class ClasspathHacker {
/**
* Add a file to the classpath.
*
* @param pPath
* path to the file
*/
public static void addFile(String pPath) {
File f = new File(pPath);
addFile(f);
}
/**
* Add a file to the classpath.
*
* @param pFile
* the file to be added
*/
public static void addFile(File pFile) {
try {
addURL(pFile.toURI().toURL());
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
/**
* Add the content pointed to by a URL to the classpath.
*
* @param pURL
* the URL pointing to the content to be added
*/
public static void addURL(URL pURL) {
/*
* Use reflection to call addURL on the system classloader, which we
* expect to be a URLClassLoader
*/
Method method;
try {
method = URLClassLoader.class.getDeclaredMethod("addURL",
new Class[] { URL.class });
method.setAccessible(true);
method.invoke(ClassLoader.getSystemClassLoader(),
new Object[] { pURL });
} catch (Exception e) {
e.printStackTrace();
}
}
}
【讨论】: