Tomcat载入器(一)
在了解tomcat的载入器时,首先需要了解的是java的类加载部分的内容。
在java体系中,系统分为3中类型的加载器
1.启动类加载器(Bootstrap ClassLoader):加载对象为java核心库,采用c/c++实现,并不继承java.lang.ClassLoader,负责加载java_home/jre/lib目录下的类库,同时也属于JVM的一部分,在JVM启动时,将被加载到内存中。启动类加载器不能被java程序直接使用。
2.扩张类加载器(Extension ClassLoader): 加载对象为java扩张库,即java_home/jre/lib/ext目录下面的类,这个类由启动类加载器加载,它的父类加载器为启动类加载器。
3.应用程序类加载器(Application ClassLoader):也成为系统类加载器(System ClassLoader)它负责加载用户路径(classpath)指定的类库,它也是有启动类加载器加载,它的父类加载被设置为启动类加载器,可以通过ClassLoader.getSystemClassLoader()获取。
tip:启动类加载器是扩展类加载器的父类加载器,并不是继承关系。
越重要的类加载器就越早被载入JVM,这是考虑到安全性,因为先加载的类加载会充当下一个类加载器的父类加载器,在双亲委托模型机制下,就能确保安全性。双亲委托模型会在类加载加载类时,首先委托父类加载器进行加载,如果父类加载器不能加载,才自己加载。这种机制有效的保证java的安全。
类加载的流程图。
下面用2个例子实现自定义的类加载器。
1.采用JVM的双亲委托机制实现。
测试类: Test.java
package com.test.load;public class Test { public Test(){ System.out.println(this.getClass().getClassLoader().toString()); }}
MyClassLoader.java
import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;public class MyClassLoader extends ClassLoader{ private String name; public MyClassLoader(ClassLoader parent, String name) { super(parent); this.name = name; } @Override public String toString() { return "test:::"+this.name; } @Override protected Class findClass(String name) throws ClassNotFoundException { InputStream is = null; byte[] data = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { is = new FileInputStream(new File("F:/work/web/Test.class")); int c = 0; while(-1 != (c = is.read())){ baos.write(c); } data = baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); }finally{ try { is.close(); baos.close(); } catch (IOException e) { e.printStackTrace(); } } return this.defineClass(name, data, 0, data.length); } public static void main(String[] args) { MyClassLoader loader = new MyClassLoader(MyClassLoader.class.getClassLoader(), "MyClassLoader"); Class clazz; try { clazz = loader.loadClass("com.test.load.Test"); Object object = clazz.newInstance(); } catch (Exception e) { e.printStackTrace(); } }}
2.采用打破委托机制。
public class MyClassLoader2 extends ClassLoader{ private String name; public MyClassLoader2(ClassLoader parent, String name) { super(parent); this.name = name; } @Override public String toString() { return "test2:::"+this.name; } @Override public Class loadClass(String name) throws ClassNotFoundException { Class clazz = null; ClassLoader systemLoader = getSystemClassLoader(); try { //打破委托机制 clazz = systemLoader.loadClass(name); } catch (Exception e) { //忽略错误 //e.printStackTrace(); } if(clazz != null) return clazz; //自己加载 clazz = findClass(name); return clazz; } @Override public Class findClass(String name) throws ClassNotFoundException { InputStream is = null; byte[] data = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { is = new FileInputStream(new File("F:/work/web/Test.class")); int c = 0; while(-1 != (c = is.read())){ baos.write(c); } data = baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); }finally{ try { is.close(); baos.close(); } catch (IOException e) { e.printStackTrace(); } } return this.defineClass(name, data, 0, data.length); } public static void main(String[] args) { MyClassLoader2 loader = new MyClassLoader2(MyClassLoader2.class.getClassLoader(), "MyClassLoader2"); Class clazz; try { clazz = loader.loadClass("com.test.load.Test"); Object object = clazz.newInstance(); } catch (Exception e) { e.printStackTrace(); } }}
还可以测试一下类加载的流程是否正确,先将Test.class文件移走,看程序是出现ClassNotFoundException异常。