----------- android培训、java培训、java博客、java学习型技术博客、期待与您交流! --------------
本章主要的知识点总结:1类加载器概念、系统默认三个类加载器及树形结构,类加载器的委托加载机制
2、掌握如何自定义类加载器的方法。
3、了解Tomcat中Servlet是如何加载的。
一.类加载器简介
1、java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器,每个加载器负责加载特定位置的类。系统默认的三个类加载器之间的父子关系:BootStrap(爷)------ExtClassLoader(父)-----AppClassLoader(子)注意:类加载器也是java类,但是这些加载器类本身又由谁来加载呢?显然必须有第一个类加载器不是java类,这个加载器就是BootStrap,BootStrp是JVM里的第一个类加载器,它不是java类,不需要被加载,它是嵌套在JVM内核里的, 它是用C++写的二进制代码。其他两个加载器都是java类。
(1)类加载器及其作用:
字节码的原始信息存放在硬盘上的classpath指定的目录下,java程序用到某个类,虚拟机要先
将该类的字节码加载到内存里,进行处理后得到的就是字节码。实现这个过程的机制就是类加载器,
其作用就是加载类。
(2)java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定
位置的类:BootStrap,ExtClassLoader,AppClassLoader
(3)类加载器的树形结构
BootStrap -------> JRE/lib/rt.jar
|
ExtClassLoader -----> JRE/lib/ext/*.jar
|
AppClassLoader -----> CLASSPATH指定的所有jar或目录
|
MyClassLoader --> 自己指定的特殊目录
二.类加载器的委托加载机制
(1)当java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
首先当前线程的类加载器去加载线程中的第一个类。
如果类A中引用了类B,java虚拟机将使用加载类A的类装载器来加载类B。
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
(2)每个类加载器加载类时,又先委托给其上级类加载器。 当所有父类包括BootTrap类加载器没有加载到类,回到发起者加载器,还加载不了,则抛出ClassNoFoundException,
(3ClassLoader类(java.lang)
类加载器是负责加载类的对象。ClassLoader 类是一个抽象类。
每个 Class 对象都包含一个对定义它的 ClassLoader 的引用。
数组类的 Class 对象不是由类加载器创建的,而是由 Java 运行时根据需要自动创建
常用方法:
protected Class<?> findClass(String name)
使用指定的二进制名称查找类。
ClassLoader getParent()
返回委托的父类加载器。
static ClassLoader getSystemClassLoader()
返回委托的系统类加载器。
Class<?> loadClass(String name)
使用指定的二进制名称来加载类。
四.自定义类加载器
(1)定义原理:
ClassLoader中的loadClass()方法是保证委托机制流程的方法,故只能继承,不能复写,查找类要用到
的findClass需要复写,将得到的class文件的内容转成字节码的方法是defineClass(),也不需要复写。
编写一个自定义的ClassLoader 对文件进行加密,解密。
- public MyClassLoader(String classDir){
- this.classDir = classDir;
- }
- public static void main(String[] args)throws Exception {
- // 从主函数的参数中获取源Class文件,和目标地址
- String srcPath = args[0];
- String desDir = args[1];
- FileInputStream fis = new FileInputStream(srcPath);
- String desFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);
- String desPath = desDir+"\\"+desFileName;
- FileOutputStream fos = new FileOutputStream(desPath);
- cypher(fis,fos);
- fis.close();
- fos.close();
- }
- //加密 解密方法
- public static void cypher(InputStream ips ,OutputStream ops)throws Exception{
- int b = 0;
- while((b = ips.read())!= -1)
- {
- ops.write(b^0xff);
- }
- }
- private String classDir;
- @Override
- // 重写findClass方法,loadClass不需要覆盖。
- protected Class<> findClass(String name)
- throws ClassNotFoundException {
- String classFileName =
- classDir+"\\"+name.substring(name.lastIndexOf("."))+1 +".class";
- try {
- FileInputStream fis = new FileInputStream(classFileName);
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- cypher(fis,bos);
- byte[] bytes = bos.toByteArray();
- //把字节数组通过defineClass方法返回Class文件,
- return defineClass(bytes, 0, bytes.length);
- } catch (Exception e) {
- e.printStackTrace();
- } finally{fis.close; bos.close()}
- return super.findClass(name);
- }
- }
五.Tomcat及一个类加载器的高级问题分析
Servlet是由Tomcat自己的类加载器加载的。
他的目录关系是
ExtClassLoader
AppClassLoader
StandardClassLoader
WebappClassLoader
在eclipse中的演示步骤:
1.创建web项目itcastweb, 创建web中的类Servlet(web中的类是特殊的类,其父类是Servlet,故可直接创建Servlet)
2.配置服务器
选择web工程-->点击Project Deployments-->Add-->Server:Tomcat 6.x -->Finish-->OK
(配置完后在目录apache-tomcat-6.0\webapps下就有itcastweb文件夹)
3启动Tomcat,即双击startup.bat
4.在浏览器中访问Tomcat服务器
http://localhost:8080/itcastweb/servlet/MyServlet(这是项目内的路径)
(查看内容:itcastweb-->webRoot-->WEB-INF-->web.xml-->source)
结果打印到了后台
5.重载Tomcat,则会在浏览器显示Tomcat自己的类加载器:WebappClassLoader
6.将MyServlet.java打成jar包输出到JDK下,重启Tomcat
MyServlet.java-->右键-->Export-->java--JAR file-->Next-->Next-->Finish
(输出的JDK版本要和Tomcat配置的JDK一致,可查看Tomcat的配置,即将startup.bat放在
编辑器中打开:set JAVA_HOME=c:\java\JDK6.0)
7.在浏览器中再次查看,报错:没找到HttpServlet.
(这是因为,这时的发起者加载器是ExtClassLoader,而HttpServlet不在Ext目录下)
8.将HttpServlet的jar包也放在Ext目录下。 重启Tomcat(即将JVM重启)
HttpServlet是Tomcat提供的,在apache-tomcat-6.0\lib\servlet-api.jar,复制到JDK6.0\jre\lib\ext下。
9.再在浏览器中访问, 此时显示的加载器便是ExtClassLoader了。