標準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); } }