常见的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。这些 SPI 的接口由 Java 核心库来提供,如 JAXP 的 SPI 接口定义包含在 javax.xml.parsers包中。
这些 SPI 的实现代码很可能是作为 Java 应用所依赖的 jar 包被包含进来,可以通过类路径(CLASSPATH)来找到,如实现了 JAXP SPI 的 ApacheXerces所包含的 jar 包。
SPI 接口中的代码经常需要加载具体的实现类。如 JAXP 中的javax.xml.parsers.DocumentBuilderFactory类中的 newInstance()方法用来生成一个新的 DocumentBuilderFactory的实例。
这里的实例的真正的类是继承自 javax.xml.parsers.DocumentBuilderFactory,由 SPI 的实现所提供的。如在 Apache Xerces 中,实现的类是org.apache.xerces.jaxp.DocumentBuilderFactoryImpl。
而问题在于,SPI 的接口是 Java 核心库的一部分,是由引导类加载器来加载的;SPI 实现的 Java 类一般是由系统类加载器来加载的。引导类加载器是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。
它也不能代理给系统类加载器,因为它是系统类加载器的祖先类加载器。也就是说,类加载器的代理模式无法解决这个问题。
线程上下文类加载器正好解决了这个问题。
如果不做任何的设置,Java 应用的线程的上下文类加载器默认就是系统上下文类加载器。
在 SPI 接口的代码中使用线程上下文类加载器,就可以成功的加载到 SPI 实现的类。线程上下文类加载器在很多 SPI 的实现中都会用到。
在5.自定义类加载器中我们的自定义类加载器CustomCL, 如果放到tomcat下的web应用中去使用会出现什么问题呢, 例如在自定义加载器 待加载的类中使用第三方类, 这个时候自定义加载器不能加载的类会交由系统加载器加载, 而该第三类不存在于类路径中, 只存在于该webApp下, 显然是加载不到的, 为了解决这个问题, 这个时候我们就需要上下文类加载器来解决这个问题了。
Webapp 类装载器:
应用层的类装载器,每个应用程序都会创建一个单独的类装载器。该类装载器只能本应用程序中可见。
所有/WEB-INF/classes目录下未压缩的类文件,资源文件都由该类装载器加载。
所有/WEB-INF/lib目录下压缩后Jar/zip文件都由该类装载器加载
显然上面我们说到的问题应该用webapp类加载器来加载第三方类, 那我们在自定义类加载器中如何获得webapp类加载器呢, 在tomcat6中启动webapp线程的上下文类加载器被设置为webapp类加载器了, 所以我们可以通过如下代码来完成加载。
protected Class loadClass(String name, boolean resolve)
throwsClassNotFoundException {
Class cls = null;
cls =findLoadedClass(name);
if(!this.dynaclazns.contains(name)&& cls == null)
cls =getSystemClassLoader().loadClass(name);
if (cls == null)
//自定义加载器和系统加载器均不能正常加载的类, 交由上下文加载器加载
cls = Thread.currentThread().getContextClassLoader().loadClass(name);
if(cls == null)
throw newClassNotFoundException(name);
if (resolve)
resolveClass(cls);
return cls;
}
其实ContextClassLoader就是Thread的一个属性而已, 我们当然可以不使用ContextClassLoader, 自己找个地方把classLoader保存起来, 在需要获取的时候能得到此classLoader就可以。
1.自定义类加载器的其他应用
热加载
每次创建一个新的类加载器, 我们修改下上面示例中的ClassIdentity类, 让他可以实现热加载。
public class ClassIdentity extends Thread {
public static voidmain(String[] args) {
newClassIdentity().start();
}
public void run() {
while(true) {
this.testClassIdentity();
try {
Thread.sleep(30 *1000L);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
public voidtestClassIdentity() {
String classDataRootPath ="C:\\Documents and Settings\\Administrator\\workspace\\Classloader\\classData";
FileSystemClassLoaderfscl1 = new FileSystemClassLoader(classDataRootPath);
String className ="com.example.Sample";
try {
Class class1= fscl1.loadClass(className);
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行该代码, 在运行过程中我们修改Sample类, 并覆盖原Sample类。
2. 类加密
指一般意义上的加密, 通过自定义加载器解密载入加密类
3. 应用隔离
非常典型的应用就是web容器
参考文件地址
http://blog.csdn.net/xyang81/article/details/7292380
http://weli.iteye.com/blog/1682625
http://my.oschina.net/huzorro/blog/96791
http://www.cnblogs.com/ericchen/archive/2011/01/15/1936130.html