Java 1.2引入了扩展机制(the extension mechanism)。
Extensions 是一组packages和classes,通过the extension mechanism来增加Java平台功能。 扩展机制让我们运行应用时,不用在classpath写extension classes的路径,就能让Java运行环境查找和装载这些extension classes。 Extension classes 如同Java 平台的core classes一样。 这就是为啥叫他们extentions的原因 --它们扩展Java的core API。
如上图所示,extensions充当Java平台的插件模块。他们的classes 和 public APIs可被平台上的所有应用使用。
扩展机制同时还为applets提供了下载远程extension classes的机制。
创建和使用Extensions
任意的packages或者classes都可以充当一个extension。Extensions用Jar文件来包装。可以通过如下2种方式来扩展extensions:
- install extensions - 把这个extension文件放到JRE的特殊的文件夹下
- download extensions - 把另一个jar文件的Manifest中引用这个extension文件。
Install extensions
把jar文件放到 <jdk_home>/jre/lib/ext文件夹下。
在Java 6 中,extension的搜索路径(java.ext.dirs)可以是多个,所以extension jar文件可以放置在任意文件路径下,没有上面必须放置在jre/lib/ext目录下的限制。这将是机器上的所有JRE都可以共享同一个扩展。
Download extensions
一个jar文件的Mainfest可以通过下面2中方式,在其headers中引用1个或多个extensions:
- Class-Path
- Extension-List
这2种方式是互斥的,也就是只能在headers使用任一种方式。Class-Path的方式不会把extension装在jre/lib/ext目录下,所以它每次都会下载所需的extensions,并且部署简单。 Extension-List方式只会在第一次下载extensions,然后安装在jre/lib/ext目录。缺点是部署系统时常常增加复杂性。
例如:
Class-Path: myapp/area.jar servlet.jar images/
在Class-Path中,任何不是以“/”结尾的路径,都被认作jar文件。以“/”结尾的路径表明时文件目录。
注:
- 在Manifest文件中,只可有一个Class-Path 头信息,并且每行长度不能超过72个字符长度。
- extensions的Manifest中也可以包含Class-Path头信息,也就是extensions可以是链状层次结构(daisy chained)。
Extension-List的例子请参考: http://java.sun.com/docs/books/tutorial/ext/basics/download.html
Extensions 的类加载策略
Java 的extension 框架采用委派方式加载类。当JRE需要加载一个类时,它按如下的路径顺序寻找该类:
- Bootstrap classes:rt.jar、i18n.jar等中的类。
- Installed extensions:先找java.ext.dirs路径中的类(install extensions),比如lib/ext目录下的类,然后再找download extensions。
- Class path:java.class.path路径下的类。
Java 类加载策略
Java平台用委派模型来加载类。每个类加载器都有一个“父”类加载器。当其需要加载一个类时,它首先从自己开始,层层向上委托检查是否已经加载,如果检查到,直接返回该类。如果没有查到,从根加载器(bootstrap)开始,层层向下委托,尝试加载。如果加载成功,返回该类。 如果最终所有的类加载器没有加载成功,抛出ClassNotFoundException异常。
如下是类加载api的一些提要:
- java.lang.ClassLoader及其子类的构造函数,在初始化时都允许指定一个“父”类加载器。如果没有指定,JVM会用system类加载器作为其默认的“父”类加载器。
- loadClass()方法执行如下任务:
- 如果这个类已经被加载,直接返回该类。
- 否则,委托让其“父”类加载器去加载该类(即调用父类的loadClass()方法)。
- 如此递归,如果“父”及其“祖先”类加载器加载该类失败,调用findClass()去查找并加载该类。
- findClass() 方法在其“父”及其“祖先”类加载器加载该类失败的情况下,才会自己查找该类。你可能要覆盖这个方法,如果你的应用需要开发使用自己的加载器的话。
- java.net.URLClassLoader作为extensions和其他jar文件的基础加载器类。它覆盖了java.lang.ClassLoader的findClass()方法,从而实现从1个或多个URL去查找类。
附: ClassLoader.loadClass()的源码
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
// Returns the class with the given binary name if this
// loader has been recorded by the Java virtual machine as an initiating
// loader of a class with that binary name. Otherwise
// null is returned.
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
类加载和java命令
- 执行Java的命令时,如果指定-classpath或-cp命令行参数的值,会覆盖环境变量CLASSPATH的值。
- -jar 选项:如果通过java -jar 来运行一个可执行的jar包,这当前jar包会覆盖CLASSPATH的值。换句话说,-jar 后面所跟的jar包的优先级别最高,如果指定了-jar选项,所有环境变量和命令行制定的搜索路径都将被忽略。JVM APPClassloader将只会以jar包为搜索范围。