为什么要自定义类加载器
在某些框架内进行中间件与应用的模块隔离,把类加载到不同的环境,如Tomcat这类Web应用服务器,内部自定义了好几种类加载器,用于隔离同一个Web应用服务器上的不同应用程序
类的加载模型并非强制,除Bootstrap外,其他的加载并非一定要引入,根据实际情况在某个时间点进行按需进行动态加载
Java代码容易被编译和篡改,可以进行编译加密,那么类加载也需要自定义,还原加密的字节码
常见的场景
实现类似进程内隔离,类加载器实际上用作不同的命名空间,以提供类似容器、模块化的效果,如两个模块依赖于某个类库的不同版本,如果分别被不同的容器加载,就可以互不干扰,这个方面集大成者是JavaEE和OSGI,JPMS等框架 应用需要从不同的数据源获取类定义信息,如网络数据源,而不是本地文件系统,或者是需要自己操纵字节码,动态修改或生成类型
注意
在一般情况下,使用不同的类加载器去加载不同的功能模块,会提高应用程序的安全性,但是如果涉及Java类型转换,则加载器类反而容易产生不美好的事情,在做Java类型转换时,只有两个类型都是由同一个加载器所加载,才能进行类型转换,否则转换时会发生异常
类加载器
实现方式
Java提供了抽象类java.lang.ClassLoader,所有用户自定义的类加载器都应该继承ClassLoader类 在自定义ClassLoader的子类时,我们常见有两种做法
重写loadClass()方法 重写findClass()方法–>推荐
对比
不建议直接修改loadClass(),而是在findClass里重写自定义类的加载方法,根据参数指定类的名字,返回对应的Class对象引用
loadClass()这个方法是实现双亲委派模型的地方,擅自修改这个方法会导致模型被破坏,容易造成问题,因此我们最好是在双亲委派模型框架下进行小范围的改动,不破坏原有的稳定结构,同时,也避免了自己重写loadClass方法的过程中必须写双亲委托的重复代码,从代码的复用性来看,不直接修改这个方法始终是比较好的选择 当编写好自定义类加载器后,便可以在程序中调用loadClass方法实现类加载
说明
其父类加载器是系统类加载器 JVM中所有类加载都会使用java.lang.ClassLoader.loadClass(String)接口(自定义类加载器并重写java.lang.ClassLoader.loadClass(String)接口除外),连JDK的核心类库也不能例外
package com. chapter11 ; import java. io. BufferedInputStream ;
import java. io. ByteArrayOutputStream ;
import java. io. FileInputStream ;
import java. io. IOException ;
public class MyClassLoader extends ClassLoader { private String byteCodePath; public MyClassLoader ( String byteCodePath) { this . byteCodePath = byteCodePath; } public MyClassLoader ( ClassLoader parent, String byteCodePath) { super ( parent) ; this . byteCodePath = byteCodePath; } @Override protected Class < ? > findClass ( String className) throws ClassNotFoundException { BufferedInputStream bis = null ; ByteArrayOutputStream baos = null ; try { String fileName = byteCodePath + className + ".class" ; bis = new BufferedInputStream ( new FileInputStream ( fileName) ) ; baos = new ByteArrayOutputStream ( ) ; int len; byte [ ] data = new byte [ 1024 ] ; while ( ( len = bis. read ( data) ) != - 1 ) { baos. write ( data, 0 , len) ; } byte [ ] byteCodes = baos. toByteArray ( ) ; Class < ? > clazz = defineClass ( null , byteCodes, 0 , byteCodes. length) ; return clazz; } catch ( IOException e) { e. printStackTrace ( ) ; } finally { try { if ( baos != null ) { baos. close ( ) ; } if ( bis != null ) { bis. close ( ) ; } } catch ( IOException e) { e. printStackTrace ( ) ; } } return null ; }
}
public class MyClassLoaderTest { public static void main ( String [ ] args) { MyClassLoader loader = new MyClassLoader ( "d:/" ) ; try { Class < ? > clazz = loader. loadClass ( "JavapTest" ) ; System . out. println ( "加载此类的加载器为:" + clazz. getClassLoader ( ) . getClass ( ) . getName ( ) ) ; System . out. println ( "加载当前JavapTest类的加载器的父类加载器为:" + clazz. getClassLoader ( ) . getParent ( ) . getClass ( ) . getName ( ) ) ; } catch ( ClassNotFoundException e) { e. printStackTrace ( ) ; } }
}
加载此类的加载器为:com. chapter11. MyClassLoader
加载当前JavapTest 类的加载器的父类加载器为:sun. misc. Launcher $AppClassLoader