标准JVM的3个classloader以及原理就很少说了. Tomcat本身的classloader是WebAppClassLoader. 每一个WEB应用运行与本身的WebAppClassLoader中.在以前,这个classloader和system classloader之间还有一个tomcat的StandardClassLoader. 但在新的版本中,standardclassloader已经废除了. WebAppClassLoader的parent就直接是JVM的system classloader. java
照例,为了防止web里的class覆盖了system里面的标准class, web classloader依然是parent优先加载的方式. WebAppClassLoader主要是从2个地方搜索class文件. WEB-INF/classes, WEB-INF/lib. 其中classes的优先级要高,这意味着,若是lib中的某个jar包含着和classes下面一样的class文件, classes下面的class将被加载. web
另外,两个并行的webappclassloader加载了一样的class,它们能够在各自的classloader命名空间下正常工做,而相互不打扰. tomcat
最后,parent classloader是没法load到child classloader下的class的, 这怎么办? 因而有了Thread.currentThread().getContextClassLoader(). 使用它去load本身看不见的class. 在这以前,每一个thread要适当正确的设置setContextClassLoader(). app
以上理论都是常识了.我只是想写点代码,模拟webappclassloader. 使用本身的classloader来测试一些问题各类class 冲突问题. dom
1, 它有一个工做目录.
2, 它搜索工做目录下的两个子目录classes和classes2,从中加载class. 其中classes优先级高于classes2. eclipse
实现方法:
1, 建立两个java project. 一个用来实现classloader. 另外一个用来写classloader要加载的classes.
2, 两个项目使用interface Test做为结合.classloader会加载一个Test的实现类,而后将其实例化并调用其test()方法.
3, Main方法了面将启动两个classloader, 分别加载本身的Test类. 它们能够互不干扰的各自工做.每一个Test都会调用本身的Printer来打印本身的static field. 能够看到一个现象,虽然两个classloader加载了一样的类,两个类的static field都各自拥有,互不干扰.它们是不共享的. webapp
运行方法:
1, 首先为即将运行的两个classloader分别建立工做目录, work1和work2. 在每一个工做目录下再建立classes和classes2目录.
2, 将接口Test.java和两个实现类Printer.java,TestImpl.java下载下来,放入一个java project.编译好之后,将其class文件复制到每一个工做目录的classes或者classes2下面.
3, 将接口Test.java,MyClassLoader.java和main函数所在类Go.java下载下来,放入eclipse project中.
4, 修改Go文件里面的工做目录路径.传递正确的值给两个classLoader.
5, 运行Go.
6, 修改Go,运行Go,以达到各类测试目的. ide
接口Test.java
函数
package classLoader; public interface Test { public void test() throws InterruptedException; }MyClassLoader.java
package classLoader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; public class MyClassLoader extends URLClassLoader { // System class loader, or so called application class loader. private ClassLoader system; // The working directory of this class loader. Recources and classes // are loaded from here. private String workDir; // Loaded class cache private class Entry{ String name; Class loadedClass; long modifiedDate; String path; } private Map<String, Entry> cache; public MyClassLoader(String workDir) { super(new URL[0]); this.workDir = workDir; this.system = this.getSystemClassLoader(); this.cache = new HashMap<String, Entry>(); } @Override public Class<?> loadClass(String name) throws ClassNotFoundException { Class<?> clazz = null; // (0) Check our previously loaded local class cache clazz = findLoadedClass0(name); if (clazz != null) { return (clazz); } // (0.1) Check our previously loaded class cache clazz = findLoadedClass(name); if (clazz != null) { return (clazz); } // (0.2) Try loading the class with the system class loader, to prevent // the webapp from overriding J2SE classes try { clazz = system.loadClass(name); if (clazz != null) { return (clazz); } } catch (ClassNotFoundException e) { // Ignore } // (1) Search local repositories clazz = findClass(name); return clazz; } // search all classes under <workDir>/classes and <workDir>/classes2. @Override public Class<?> findClass(String name) throws ClassNotFoundException { Class<?> clazz; // search classes first. String path = this.workDir+"classes/"+name.replace('.', '/')+".class"; File file = new File(path); if (file.exists()) { ByteBuffer buffer = ByteBuffer.allocate((int)file.length()+1000); try { new FileInputStream(file).getChannel().read(buffer); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } buffer.flip(); clazz = this.defineClass(name, buffer.array(), 0, (int)file.length()); Entry entry = new Entry(); entry.loadedClass = clazz; entry.name = name; entry.modifiedDate = file.lastModified(); entry.path = file.getAbsolutePath(); // put it into cache. this.cache.put(name, entry); return clazz; } // search classes2 secondly. path = this.workDir+"classes2/"+name.replace('.', '/')+".class"; file = new File(path); if (file.exists()) { ByteBuffer buffer = ByteBuffer.allocate((int)file.length()+1000); try { new FileInputStream(file).getChannel().read(buffer); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } buffer.flip(); clazz = this.defineClass(name, buffer.array(), 0, (int)file.length()); Entry entry = new Entry(); entry.loadedClass = clazz; entry.name = name; entry.modifiedDate = file.lastModified(); entry.path = file.getAbsolutePath(); this.cache.put(name, entry); return clazz; } throw new ClassNotFoundException(); } protected Class<?> findLoadedClass0(String name) { Entry entry = cache.get(name); if (entry != null && !this.isModified(entry)) { return entry.loadedClass; } return (null); } private boolean isModified(Entry entry) { if (new File(entry.path).lastModified() != entry.modifiedDate) return true; return false; } }Go.java
package classLoader; public class Go { public static void main(String[] args) throws Exception { ClassLoader cl = new MyClassLoader("work1/"); Class<?> clazz = cl.loadClass("myClasses.TestImpl"); Test test = (Test)clazz.newInstance(); test.test(); ClassLoader cl2 = new MyClassLoader("work2/"); Class<?> clazz2 = cl2.loadClass("myClasses.TestImpl"); Test test2 = (Test)clazz2.newInstance(); test2.test(); test.test(); test2.test(); Thread.currentThread().setContextClassLoader(cl2); } }以上是模拟webappclassloader. 下面将模拟web实现
TestImpl.java
测试
package myClasses; import java.util.Random; import classLoader.Test; public class TestImpl implements Test { public static int number = -1; private Printer printer; public TestImpl() { printer = new Printer(); } public void test() throws InterruptedException { if (number<0) number = Math.abs(new Random().nextInt(30)); Thread.yield(); Thread.sleep(1000); Thread.yield(); printer.print(); } }Printer.java
package myClasses; public class Printer { public void print() { System.out.println("Classloader is: " + this.getClass().getClassLoader()); System.out.println("The number is: " + TestImpl.number); } }