Java教程是为JDK 8编写的。本页面中描述的示例和实践不利用后续版本引入的改进,并可能使用不再可用的技术。
请参阅Java语言变更以了解Java SE 9及其后续版本中更新的语言功能的摘要。
请参阅JDK发布说明以获取有关所有JDK版本的新功能、增强功能和已删除或不推荐选项的信息。
JarClassLoader类扩展了java.net.URLClassLoader类。如其名所示,URLClassLoader被设计用于通过搜索一组URL来加载类和资源。这些URL可以指向目录或JAR文件。
除了子类化URLClassLoader,JarClassLoader还利用了另外两个新的与JAR相关的API,即java.util.jar包和java.net.JarURLConnection类。在本节中,我们将详细讨论JarClassLoader的构造函数和两个方法。
构造函数以java.net.URL实例作为参数。传递给此构造函数的URL将在JarClassLoader中的其他地方用于查找要加载类的JAR文件。
public JarClassLoader(URL url) { super(new URL[] { url }); this.url = url; }
URL对象被传递给父类URLClassLoader的构造函数,该构造函数接受一个URL[]数组作为参数,而不是单个URL实例。
一旦使用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处理功能:
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上。
该类表示应用程序与JAR文件之间的通信链接。它具有访问JAR文件清单的方法。在getMainClassName的第二行中:
JarURLConnection uc = (JarURLConnection)u.openConnection();
在此语句中,第一行创建的URL实例打开了一个URLConnection。然后将URLConnection实例强制转换为JarURLConnection,以便利用JarURLConnection的JAR处理功能。
通过使用JarURLConnection打开到JAR文件的链接,您可以使用JarURLConnection的getMainAttributes方法访问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_VERSION、CLASS_PATH和SEALED等静态字段,用于指定其他标准清单标头。)
我们已经了解了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的教程来复习反射。