Java类加载器学习总结Word文件下载.docx

上传人:b****1 文档编号:1465217 上传时间:2023-04-30 格式:DOCX 页数:11 大小:84.56KB
下载 相关 举报
Java类加载器学习总结Word文件下载.docx_第1页
第1页 / 共11页
Java类加载器学习总结Word文件下载.docx_第2页
第2页 / 共11页
Java类加载器学习总结Word文件下载.docx_第3页
第3页 / 共11页
Java类加载器学习总结Word文件下载.docx_第4页
第4页 / 共11页
Java类加载器学习总结Word文件下载.docx_第5页
第5页 / 共11页
Java类加载器学习总结Word文件下载.docx_第6页
第6页 / 共11页
Java类加载器学习总结Word文件下载.docx_第7页
第7页 / 共11页
Java类加载器学习总结Word文件下载.docx_第8页
第8页 / 共11页
Java类加载器学习总结Word文件下载.docx_第9页
第9页 / 共11页
Java类加载器学习总结Word文件下载.docx_第10页
第10页 / 共11页
Java类加载器学习总结Word文件下载.docx_第11页
第11页 / 共11页
亲,该文档总共11页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

Java类加载器学习总结Word文件下载.docx

《Java类加载器学习总结Word文件下载.docx》由会员分享,可在线阅读,更多相关《Java类加载器学习总结Word文件下载.docx(11页珍藏版)》请在冰点文库上搜索。

Java类加载器学习总结Word文件下载.docx

综上所述,它们之间的关系可以通过下图形象的描述:

双亲委托模型

Java中ClassLoader的加载采用了双亲委托机制,采用双亲委托机制加载类的时候采用如下的几个步骤:

1当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。

每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载时可以直接返回。

2当前classLoader的缓存中没有找到被加载的类时,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrpClassLoader。

3当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回。

线程上下文类加载器

ContextClassLoader是java.lang.Thread类的一个属性,Thread类中的方法getContextClassLoader()和setContextClassLoader(ClassLoadercl)用来获取和设置线程的上下文类加载器。

如果没有通过setContextClassLoader(ClassLoadercl)方法进行设置的话,线程将继承其父线程的上下文类加载器。

Java应用运行的初始线程的上下文类加载器是系统类加载器(AppClassLoader),在线程中运行的代码可以通过此类加载器来加载类和资源。

双亲委托模型并不能解决Java应用开发中会遇到的类加载器的全部问题。

Java提供了很多服务提供者接口(ServiceProviderInterface,SPI),允许第三方为这些接口提供实现。

常见的SPI有JDBC、JCE、JNDI、JAXP和JBI等。

这些SPI的接口由Java核心库来提供,如JAXP的SPI接口定义包含在javax.xml.parsers包中(rt.jar)。

这些SPI的实现代码很可能是作为Java应用所依赖的jar包被包含进来,可以通过类路径(CLASSPATH)来找到,如实现了JAXPSPI的ApacheXerces所包含的jar包。

SPI接口中的代码经常需要加载具体的实现类。

如JAXP中的javax.xml.parsers.DocumentBuilderFactory类中的newInstance()方法用来生成一个新的DocumentBuilderFactory的实例。

这里的实例的真正的类是继承自javax.xml.parsers.DocumentBuilderFactory,由SPI的实现所提供的。

如在ApacheXerces中,实现的类是org.apache.xerces.jaxp.DocumentBuilderFactoryImpl。

而问题在于,SPI的接口是Java核心库的一部分,是由引导类加载器(BootstrpClassLoader)来加载的;

SPI实现的Java类一般是由系统类加载器(APPClassLoader)来加载的。

引导类加载器是无法找到SPI的实现类的,因为它只加载Java的核心库。

它也不能代理给系统类加载器,因为它是系统类加载器的祖先类加载器。

也就是说,类加载器的双亲委托模型无法解决这个问题。

线程ContextClassLoader正好解决了这个问题。

如果不做任何的设置,Java应用的线程的ContextClassLoader默认就是系统上下文类(AppClassLoader)加载器。

在SPI接口的代码中使用线程ContextClassLoader,就可以成功的加载到SPI实现的类。

线程ContextClassLoader在很多SPI的实现中都会用到。

以下是一个简单的Java应用,用于测试ContextClassLoader。

importjavax.xml.parsers.DocumentBuilderFactory;

publicclassTestClassLoader{

publicstaticvoidmain(String[]args)throwsException{

TestClassLoadertest=newTestClassLoader();

test.testLoad();

}

privatevoidtestLoad()throwsException{

//1

System.out.println(Thread.currentThread());

//Thread[main,5,main]

//2

System.out.println(Thread.currentThread().getContextClassLoader());

//sun.misc.Launcher$AppClassLoader@19821f

//3

DocumentBuilderFactoryfactory=DocumentBuilderFactory.newInstance(

"

org.apache.xerces.jaxp.DocumentBuilderFactoryImpl"

null);

}

注释//2显示当前主线程使用的上下文类加载器是AppClassLoader,注释//3由DocumentBuilderFactory负责加载并实例化它的一个实现类,该实现类由ApacheXerces提供,同时在newInstance中传入了一个空的加载器。

publicclassDocumentBuilderFactory{

publicstaticDocumentBuilderFactorynewInstance(StringfactoryClassName,ClassLoaderclassLoader){

try{

return(DocumentBuilderFactory)FactoryFinder.newInstance(factoryClassName,classLoader,false);

}catch(FactoryFinder.ConfigurationErrore){

thrownewFactoryConfigurationError(e.getException(),e.getMessage());

}

   }

   //......

DocumentBuilderFactory中的newInstance方法将加载的任务交给FactoryFinder来完成。

classFactoryFinder{

staticObjectnewInstance(StringclassName,ClassLoadercl,booleandoFallback)

throwsConfigurationError

{

//使用给定的加载器cl,获取DocumentBuilderFactoryImpl类的Class对象,此处的cl为null

ClassproviderClass=getProviderClass(className,cl,doFallback);

//创建DocumentBuilderFactoryImpl的实例

Objectinstance=providerClass.newInstance();

returninstance;

catch(ClassNotFoundExceptionx){

thrownewConfigurationError(

"

Provider"

+className+"

notfound"

x);

catch(Exceptionx){

couldnotbeinstantiated:

+x,

x);

   }

   //......

获取DocumentBuilderFactoryImpl实现类的Class对象在getProviderClass方法中完成。

staticprivateClassgetProviderClass(StringclassName,ClassLoadercl,

booleandoFallback)throwsClassNotFoundException

if(cl==null){

//1

cl=ss.getContextClassLoader();

thrownewClassNotFoundException();

else{

//2由上下文类加载器完成实现类的加载

returncl.loadClass(className);

catch(ClassNotFoundExceptione1){

//......

//......

由于cl传入的是一个null值,所以需要从ss对象中获取一个上下文类加载器,那ss对象中的getContextClassLoader方法如何获取一个上下文加载器的呢?

classSecuritySupport{

ClassLoadergetContextClassLoader()throwsSecurityException{

return(ClassLoader)

AccessController.doPrivileged(newPrivilegedAction(){

publicObjectrun(){

ClassLoadercl=null;

cl=Thread.currentThread().getContextClassLoader();

if(cl==null)

cl=ClassLoader.getSystemClassLoader();

returncl;

});

该方法中的其他代码不需要管,由注释//1处可以看出最后return语句返回的cl加载器,就是从当前线程中获得的上下文类加载器。

使用myeclipse调试跟踪代码可以看出,当前只有一个mian主线程。

主线程中的上下文类加载器默认就是AppClassLoader。

方法getProviderClass()中的注释//2处使用AppClassLoader加载器来完成实现类的加载。

在注释//2处为什么不使用FactoryFinder.Class.getClassLoader()来完成实现类的加载呢?

因为FactoryFinder类位于核心类库中,由Bootstrp类加载器完成加载,而实现类DocumentBuilderFactoryImpl在我们自己的java应用中,在Bootstrp加载器的搜索范围中,找不到该实现类,因此这样的方式无法完成类的加载。

另外一个原因,即使将实现类放到Bootstrp加载器的搜索范围中,也是不能完成加载,因为Bootstrp只负责加载固定的核心类库,其他的字节码文件一律都不加载。

正常的双亲委派模型中,下层的类加载器可以使用上层父加载器加载的对象,但是上层父类的加载器不可能使用子类加载的对象(因为,正常情况下加载器只会向上搜索被加载的类,不会向下搜索)。

而有些时候程序的确需要上层调用下层,这时候就需要线程上下文类加载器来处理,以上就是这样的一个例子。

自定义类加载器

虽然在绝大多数情况下,系统默认提供的类加载器实现已经可以满足需求。

但是在某些情况下,您还是需要为应用开发出自己的类加载器。

比如您的应用通过网络来传输Java类的字节代码,为了保证安全性,这些字节代码经过了加密处理。

这个时候您就需要自己的类加载器来从某个网络地址上读取加密后的字节代码,接着进行解密和验证,最后定义出要在Java虚拟机中运行的类来。

下面将通过具体的实例来说明类加载器的开发。

文件系统类加载器

第一个类加载器用来加载存储在文件系统上的Java字节代码。

完整的实现下所示。

publicclassFileSystemClassLoaderextendsClassLoader{

privateStringrootDir;

publicFileSystemClassLoader(StringrootDir){

this.rootDir=rootDir;

protectedClass<

?

>

findClass(Stringname)throwsClassNotFoundException{

byte[]classData=getClassData(name);

if(classData==null){

thrownewClassNotFoundException();

}else{

returndefineClass(name,classData,0,classData.length);

}

privatebyte[]getClassData(StringclassName){

Stringpath=classNameToPath(className);

try{

InputStreamins=newFileInputStream(path);

ByteArrayOutputStreambaos=newByteArrayOutputStream();

intbufferSize=4096;

byte[]buffer=newbyte[bufferSize];

intbytesNumRead=0;

while((bytesNumRead=ins.read(buffer))!

=-1){

baos.write(buffer,0,bytesNumRead);

}

returnbaos.toByteArray();

}catch(IOExceptione){

e.printStackTrace();

returnnull;

privateStringclassNameToPath(StringclassName){

returnrootDir+File.separatorChar

+className.replace('

.'

File.separatorChar)+"

.class"

;

类FileSystemClassLoader继承自类java.lang.ClassLoader。

一般来说,自己开发的类加载器只需要覆写findClass(Stringname)方法即可。

java.lang.ClassLoader类的方法loadClass()封装了前面提到的双亲委托机制的实现。

loadClass()方法会首先调用findLoadedClass()方法来检查该类是否已经被加载过;

如果没有加载过的话,会调用父类加载器的loadClass()方法来尝试加载该类;

如果父类加载器无法加载该类的话,就调用findClass()方法来查找该类。

因此,为了保证类加载器都正确实现双亲委托机制,在开发自己的类加载器时,最好不要覆写loadClass()方法,而是覆写findClass()方法。

类FileSystemClassLoader的findClass()方法首先根据类的全名在硬盘上查找类的字节代码文件(.class文件),然后读取该文件内容,最后通过defineClass()方法来把这些字节代码转换成java.lang.Class类的实例。

 

参考

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 人文社科 > 法律资料

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2