深入分析ClassLoader工作机制.docx
- 文档编号:6990109
- 上传时间:2023-01-15
- 格式:DOCX
- 页数:17
- 大小:66.89KB
深入分析ClassLoader工作机制.docx
《深入分析ClassLoader工作机制.docx》由会员分享,可在线阅读,更多相关《深入分析ClassLoader工作机制.docx(17页珍藏版)》请在冰豆网上搜索。
深入分析ClassLoader工作机制
深入分析ClassLoader工作机制
一层一层判断是否应该由本层ClassLoader加载,并通过在哪一层加载确定类的加载级别。
BootstrapClassLoader:
主要加载JVM自身工作需要的类,这个ClassLoader完全由JVM自己控制的。
不准守普通的加载规则,没有父类和子类。
sun.misc.LauncherJava程序的入口就是sun.misc.Launcher,jdk的扩展类加载器ExtClassLoader和系统类加载器AppClassLoader都是Launcher的内部类。
Launcher初始化extensionclassloader,systemclassloader,并将systemclassloader设置成为contextclassloader,但是仅仅返回systemclassloader给JVM。
下列代码是Launcher构建的过程。
publicLauncher(){
Launcher.ExtClassLoadervar1;
...
var1=Launcher.ExtClassLoader.getExtClassLoader();
...
this.loader=Launcher.AppClassLoader.getAppClassLoader(var1);
....
}
可以看到APPClassLoader和ExtClassLoader都继承自URLClassLoader,在构建Launcher过程中,先创建ExtClassLoader,然后根据ExtClassLoader作为父加载器创建APPClassLoader。
然后使用Launcher的getLauncher方法得到的就是APPClassLoader。
如果在Java应用中没有其他的ClassLoader,则除了”java.ext.dirs”目录下的类由ExtClassLoader加载的外,其他的都是由APPClassLoader。
看一下源码。
ExtClassLoader:
publicstaticLauncher.ExtClassLoadergetExtClassLoader()throwsIOException{
finalFile[]var0=getExtDirs();//这里
try{
return(Launcher.ExtClassLoader)AccessController.doPrivileged(newPrivilegedExceptionAction(){
publicLauncher.ExtClassLoaderrun()throwsIOException{
intvar1=var0.length;
for(intvar2=0;var2 MetaIndex.registerDirectory(var0[var2]); } returnnewLauncher.ExtClassLoader(var0);///这里 } }); }catch(PrivilegedActionExceptionvar2){ throw(IOException)var2.getException(); } } privatestaticFile[]getExtDirs(){ Stringvar0=System.getProperty("java.ext.dirs"); ... } APPClassLoader: 使用ExtClassLoader作为父类加载器 staticclassAppClassLoaderextendsURLClassLoader{ publicstaticClassLoadergetAppClassLoader(finalClassLoadervar0)throwsIOException{ finalStringvar1=System.getProperty("java.class.path");//这里 finalFile[]var2=var1==null? newFile[0]: Launcher.getClassPath(var1); return(ClassLoader)AccessController.doPrivileged(newPrivilegedAction(){ publicLauncher.AppClassLoaderrun(){ URL[]var1x=var1==null? newURL[0]: Launcher.pathToURLs(var2); returnnewLauncher.AppClassLoader(var1x,var0);//这里 } }); } 这是URLClassLoader的: publicURLClassLoader(URL[]urls,ClassLoaderparent,....){} 加载Class文件到内存中的2种方式 一种是隐式加载,一种是显式加载。 隐式加载: 就是不需要在代码中调用ClassLoader来加载的类,而是通过JVM来自动加载这些需要的类到内存中的方式,比如在我们继承或引用某些类的时候,JVM分析当前类引用的类不在内存中,那么就会自动加载类到内存中。 显示加载: 就是在代码中调用ClassLoader来加载一个类,比如this.getClass().getClassLoader().loadClass()或者Class.forName()或者自定义的ClassLoader的loadClass方法。 混合的方式,就是在自定义ClassLoader时候引用其他类,就算是隐式加载。 加载class的过程 加载过程,简单地看了一下代码。 大概是: —>findClass(finalStringname) —>defineClass(Stringname,Resourceres) —>Class 加载字节码到内存中 在抽象类ClassLoader中,很多实现都交给子类实现,如如何找到,如何加载到内存中。 就是findClass()方法。 最常用的URLClassLoader的findClass源码如下; protectedClass >findClass(finalStringname) throwsClassNotFoundException { try{ returnAccessController.doPrivileged( newPrivilegedExceptionAction publicClassrun()throwsClassNotFoundException{ Stringpath=name.replace('.','/').concat(".class"); Resourceres=ucp.getResource(path,false); if(res! =null){ try{ returndefineClass(name,res); }catch(IOExceptione){ thrownewClassNotFoundException(name,e); } }else{ thrownewClassNotFoundException(name); } } },acc); }catch(java.security.PrivilegedActionExceptionpae){ throw(ClassNotFoundException)pae.getException(); } } 简要说明一下: 首先将Path,类名的包定义,由’.’换为路径的’/’。 并将.class加上。 然后ucp(URLClassPath类型)定义哪里找到这个class文件,读取它的字节流。 然后通过defineClass来实例类class对象。 根据URL传过来的是Jar包还是实际Class文件,就能创建不同的Loader了,这样就实现加载Class文件到内存中。 下图还可以看出Loader是URLClassPath的内部类。 常见加载类错误分析 ClassNotFoundException 这个非常常见的错误,一般问题是找不到.class文件,就是classpath路径和你想要的class文件路径不在一个地方。 如果不知道classpath路径是什么,可以通过下列代码方式找到。 this.getClass().getClassLoader().getResource("").toString(); 通常加载一个不知道的类时就会发生这样的错误。 publicclassnotFound{ publicstaticvoidmain(String[]args){ try{ Class.forName("NotFoundClass"); }catch(ClassNotFoundExceptione){ e.printStackTrace(); } } } 显示加载的几种方式: Class.forName(); ClassLoader的loadClass(); ClassLoader的findSystemClass(); UnsatisfiedLinkError 这个问题不常见,出现的场景就是调用某些本地lib中的native方法时,没有找到这样的lib。 报出的错误。 /** *文件描述: */ publicclassNoLibException{ publicnativevoidnativeMethod(); static{ System.loadLibrary("NoLib"); } publicstaticvoidmain(String[]args){ newNoLibException().nativeMethod(); } } java.lang.UnsatisfiedLinkError: noNoLibinjava.library.path atjava.lang.ClassLoader.loadLibrary(ClassLoader.java: 1886) atjava.lang.Runtime.loadLibrary0(Runtime.java: 849) atjava.lang.System.loadLibrary(System.java: 1088) atCCF.NoLibException. 11) ClassCastException 强制转换异常,比如说想把String强制转换为Integer 代码 importjava.util.HashMap; importjava.util.Map; /** *文件描述: */ publicclassCastException{ publicstaticMapm=newHashMap(){ { put("a","2"); } }; publicstaticvoidmain(String[]args){ Integerinteger=(Integer)m.get("a"); System.out.println(integer); } } 结果: Exceptioninthread"main"java.lang.ClassCastException: java.lang.Stringcannotbecasttojava.lang.Integer atCCF.CastException.main(CastException.java: 19) atsun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethod) atsun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java: 57) atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java: 43) atjava.lang.reflect.Method.invoke(Method.java: 606) atcom.intellij.rt.execution.application.AppMain.main(AppMain.java: 144) 实现自己的ClassLoader 获取类加载器 获取类加载器的实现非常简单,简单到只需要一句话,获取当前线程的ClassLoader即可。 /** *获取类加载器 * *@return */ publicstaticClassLoadergetClassLoader(){ returnThread.currentThread().getContextClassLoader(); } 根据类名称加载类 根据类名称加载类,这个方法是利用Class.forName实现的。 相当于在Class.forName修饰了一层。 实现如下: /** *加载类 * *@paramclassName类名 *@paramisInitialized此处的是否初始化标志指的是是否执行类的静态代码块 *@return */ publicstaticClass >loadClass(StringclassName,booleanisInitialized){ Class >cls; try{ cls=Class.forName(className,isInitialized,getClassLoader()); }catch(ClassNotFoundExceptione){ e.printStackTrace(); thrownewRuntimeException(e); } returncls; } 一个值得注意的点是: 参数booleanisInitialized表达的标志位指的是是否执行类的静态代码块,为了提高性能可以设置为false。 获取指定包下的所有类 获取指定包下的所有类是最麻烦的一件事情,因为包可以是file,也可是jar包。 另外在包名和实际包所在的路径需要转换,比如需要把”.”转为”/”把”%20”转换为”“;对于jar包的类,使用JarURLConnnection连接 /** *获取指定包下的所有类 * *@parampackageName *@return */ publicstaticSet >>getClassSet(StringpackageName){ Set >>classSet=newHashSet >>(); try{ /*获取资源,将包名替换为包路径*/ Enumeration while(urlEnumeration.hasMoreElements()){ URLurl=urlEnumeration.nextElement(); if(url! =null){ Stringprotocol=url.getProtocol(); /*file方式处理*/ if(protocol.equals("file")){ /*%20换成空格*/ StringpackagePath=url.getPath().replaceAll("%20",""); addClass(classSet,packagePath,packageName); }elseif(protocol.equals("jar")){/*jar包处理*/ /*打开连接*/ JarURLConnectionjarURLConnection=(JarURLConnection)url.openConnection(); if(jarURLConnection! =null){ JarFilejarFile=jarURLConnection.getJarFile(); if(jarFile! =null){ /*遍历元素*/ Enumeration while(jarEntries.hasMoreElements()){ JarEntryjarEntry=jarEntries.nextElement(); /*以class结尾*/ if(jarEntry.getName().endsWith(".class")){ /*获取类名,并将"/"转换为"."*/ StringclassName=jarEntry.getName().substring(0,jarEntry.getName().lastIndexOf(".")).replaceAll("/","."); addClassAction(classSet,className); } } } } } } } }catch(IOExceptione){ e.printStackTrace(); } returnclassSet; } /** *内部调用loadClass,并将Class添加到Set中 * *@paramclassSet *@paramclassName */ privatestaticvoidaddClassAction(Set >>classSet,StringclassName){ Class >cls=loadClass(className,false); classSet.add(cls); } /** *@paramclassSet *@parampackagePath *@parampackageName */ privatestaticvoidaddClass(Set >>classSet,StringpackagePath,StringpackageName){ File[]files=newFile(packagePath).listFiles(newFileFilter(){ /*将所有的是class文件和目录文件返回*/ publicbooleanaccept(Filefile){ return(file.isFile()&&file.getName().endsWith(".class"))||file.isDirectory(); } }); for(Filefile: files){ StringfileName=file.getName(); /*文件方式处理*/ if(file.isFile()){ StringclassName=fileName.substring(0,fileName.lastIndexOf(".")); if(packageName! =null){ className=packageName+"."+className; } addClassAction(classSet,className); }else{/*文件夹方式处理*/ StringsubPackagePath=fileName; if(packagePath! =null){ /*子目录*/ subPackagePath=packagePath+"/"+subPackagePath; } StringsubPackageName=fileName; if(packageName! =null){ /*子包名*/ subPackageName=packageName+"."+subPackageName; } /*递归处理*/ addClass(classSet,subPackagePath,subPackageName); } } } 函数注释写得很清楚,可以细细琢磨。 一个值得注意的细节,关于匿名内部类,如果要访问外部类的变量,则需要再外部变量加final修饰如下所示: 原因是执行匿名内部类的时候,形成的函数的栈帧无法访问到外部类栈帧的局部变量,所有把外部变量final化则可以访问到这个变量,具体可以参考其他文章。 只是突然想到这个知识点并稍微说说而已。 */ privatestaticvoidaddClass(Set >>classSet,StringpackagePath,finalStringpackageName){ File[]files=newFile(packagePath).listFiles(newFileFilter(){ /*将所有的是class文件和目录文件返回,一个细节,如果匿名内部类里要使用packageName则在addClass参数中要加入修饰词final *即finalStringpackageName publicbooleancept(Filefile){ return(packageName! =null&&file.isFile()&&file.getName().endsWith(".class"))||file.isDirectory(); } }); 获取类加载器 根据类名称加载类 获取指定包下的所有类 加载自定义格式的Class文件 实现一个从远端服务器,经过加密的class文件的网络传输,在本地解密,并通过defineClass方法创建这个类的实例的方法。 代码如下: importjava.io.ByteArray
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 深入 分析 ClassLoader 工作 机制