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 方法

一旦使用 Binding 了 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 文件清单的Main\-ClassHeaders 指定了 JARBinding 的应用程序的入口点。为了了解getMainClassName如何访问Main\-ClassHeaders 值,让我们详细看一下该方法,并特别注意它使用的新 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的实例,该类 MapJAR 文件清单中的 Headers 名称及其关联的字符串 值。 getMainClassName中的第三行创建一个Attributes对象:

Attributes attr = uc.getMainAttributes();

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

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

该方法的参数Attributes\.Name\.MAIN_CLASS指定它是您想要的Main\-ClassHeaders 的值。 (Attributes\.Name类还提供了MANIFEST_VERSIONCLASS_PATHSEALED之类的静态字段,用于指定其他标准清单 Headers.)

invokeClass 方法

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

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 将参数传递给该类并启动它。您可以参考反射 API上的教程以了解反射。