仿照Tomcat寫本身的classloader

標準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

個人classloader

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

}
相關文章
相關標籤/搜索