文档

Java™教程
隐藏目录
JarClassLoader类
路径: 部署
课程: JAR文件中的程序打包
章节: 使用与JAR相关的API

JarClassLoader类

JarClassLoader类扩展了java.net.URLClassLoader类。如其名所示,URLClassLoader被设计用于通过搜索一组URL来加载类和资源。这些URL可以指向目录或JAR文件。

除了子类化URLClassLoaderJarClassLoader还利用了另外两个新的与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;
}

您可能还记得在之前的课程中,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 + "!/");

该语句构造一个新的URL对象,表示一个JAR 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) {
        // 这不应该发生,因为我们已禁用了访问检查
    }
}

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

Class c = loadClass(name);

loadClass方法是从java.lang.ClassLoader继承的。

一旦主类被加载,就会使用java.lang.reflect包的反射API将参数传递给类并启动它。您可以参考反射API的教程来复习反射。


上一页:使用 JAR 相关的 API
下一页:JarRunner 类