文档

Java™ 教程-Java Tutorials 中文版
JarClassLoader 类
Trail: Deployment
Lesson: Packaging Programs in JAR Files
Section: Using JAR-related APIs

JarClassLoader 类

JarClassLoader 类继承了 java.net.URLClassLoader。顾名思义,URLClassLoader 旨在用于加载通过搜索一组 URL 访问的类和资源。URL 可以引用目录或 JAR 文件。

除了子类化 URLClassLoader 之外,JarClassLoader 还利用了其他两个与 JAR 相关的新 API,即 java.util.jar 包和 java.net.JarURLConnection 类中的功能。在本节中,我们将详细介绍构造函数和 JarClassLoader 的两个方法。

JarClassLoader 构造函数

构造函数将 java.net.URL 的实例作为参数。传递给此构造函数的 URL 将在 JarClassLoader 中的其他位置使用,以查找要从中加载类的 JAR 文件。

public JarClassLoader(URL url) {
    super(new URL[] { url });
    this.url = url;
}

URL 对象传递给超类 URLClassLoader 的构造函数,该构造函数采用 URL[] 数组,而不是单个 URL 实例,作为参数。

getMainClassName 方法

使用 JAR 绑定的应用程序的 URL 构造 JarClassLoader 对象后,需要一种方法来确定 JAR 文件中的哪个类是应用程序的入口点。这是 getMainClassName 方法的工作:

public String getMainClassName() throws IOException {
    URL u = new URL("jar", "", url + "!/");
    JarURLConnection uc = (JarURLConnection)u.openConnection();
    Attributes attr = uc.getMainAttributes();
    return attr != null
                   ? attr.getValue(Attributes.Name.MAIN_CLASS)
                   : null;
}

你可以从 previous lesson 中回想一下,JAR 绑定的应用程序的入口点是由 JAR 文件清单的 Main-Class 头指定的。要了解 getMainClassName 如何访问 Main-Class 头值,让我们详细查看该方法,特别注意它使用的新 JAR 处理功能:

JarURLConnection 类和 JAR URL

getMainClassName 方法使用 java.net.JarURLConnection 类指定的 JAR URL 格式。JAR 文件的 URL 语法如下例所示:

jar:http://www.example.com/jarfile.jar!/

终止 !/ 分隔符表示 URL 引用整个 JAR 文件。分隔符后面的任何内容都引用特定的 JAR 文件内容,如下例所示:

jar:http://www.example.com/jarfile.jar!/mypackage/myclass.class

getMainClassName 方法的第一行是:

URL u = new URL("jar", "", url + "!/");

此语句构造一个表示 JAR URL 的新 URL 对象,将 !/ 分隔符附加到用于创建 JarClassLoader 实例的 URL。

java.net.JarURLConnection 类

此类表示应用程序和 JAR 文件之间的通信链接。它具有访问 JAR 文件清单的方法。getMainClassName 的第二行是:

JarURLConnection uc = (JarURLConnection)u.openConnection();

在此语句中,在第一行中创建的 URL 实例将打开 URLConnection。然后将 URLConnection 实例强制转换为 JarURLConnection,以便它可以利用 JarURLConnection 的 JAR 处理功能。

获取清单属性:java.util.jar.Attributes

JarURLConnection 打开 JAR 文件后,你可以使用 JarURLConnectiongetMainAttributes 方法访问 JAR 文件清单中的头信息。此方法返回 java.util.jar.Attributes 的实例,该实例将 JAR 文件清单中的头名称与其关联的字符串值进行映射。getMainClassName 中的第三行创建 Attributes 对象:

Attributes attr = uc.getMainAttributes();

要获取清单的 Main-Class 头的值,getMainClassName 的第四行将调用 Attributes.getValue 方法:

return attr != null
               ? attr.getValue(Attributes.Name.MAIN_CLASS)
               : null;

方法的参数 Attributes.Name.MAIN_CLASS 指定它是你想要的 Main-Class 头的值。(Attributes.Name 类还提供静态字段,如 MANIFEST_VERSIONCLASS_PATHSEALED,用于指定其他标准清单头。)

invokeClass 方法

我们已经看到 JarURLClassLoader 如何识别 JAR 打包应用程序中的主类。要考虑的最后一个方法 JarURLClassLoader.invokeClass,可以调用该主类来启动 JAR 绑定的应用程序:

public void invokeClass(String name, String[] args)
    throws ClassNotFoundException,
           NoSuchMethodException,
           InvocationTargetException
{
    Class c = loadClass(name);
    Method m = c.getMethod("main", new Class[] { args.getClass() });
    m.setAccessible(true);
    int mods = m.getModifiers();
    if (m.getReturnType() != void.class || !Modifier.isStatic(mods) ||
        !Modifier.isPublic(mods)) {
        throw new NoSuchMethodException("main");
    }
    try {
        m.invoke(null, new Object[] { args });
    } catch (IllegalAccessException e) {
        // This should not happen, as we have disabled access checks
    }
}

invokeClass 方法有两个参数:应用程序入口点类的名称和要传递给入口点类的 main 方法的字符串参数数组。首先,加载主类:

Class c = loadClass(name);

loadClass 方法继承自 java.lang.ClassLoader

加载主类后,java.lang.reflect 包的反射 API 用于将参数传递给类并启动它。你可以参考 The Reflection API 上的教程来查看反射。


Previous page: Using JAR-related APIs
Next page: The JarRunner Class