仿照Tomcat写自己的classloader

标准JVM的3个classloader以及原理就不多说了. Tomcat自己的classloader是WebAppClassLoader. 每个WEB应用运行与自己的WebAppClassLoader中.在之前,这个classloader和system classloader之间还有一个tomcat的StandardClassLoader. 但在新的版本中,standardclassloader已经废除了. WebAppClassLoader的parent就直接是JVM的system classloader.

照例,为了防止web里的class覆盖了system里面的标准class, web classloader依然是parent优先加载的方式. WebAppClassLoader主要是从2个地方搜索class文件. WEB-INF/classes, WEB-INF/lib. 其中classes的优先级要高,这意味着,如果lib中的某个jar包含着和classes下面同样的class文件, classes下面的class将被加载.

另外,两个并行的webappclassloader加载了同样的class,它们可以在各自的classloader命名空间下正常工作,而相互不打扰.

最后,parent classloader是无法load到child classloader下的class的, 这怎么办? 于是有了Thread.currentThread().getContextClassLoader(). 使用它去load自己看不见的class. 在这之前,每个thread要适当正确的设置setContextClassLoader().

以上理论都是常识了.我只是想写点代码,模拟webappclassloader. 使用自己的classloader来测试一些问题各种class 冲突问题.

我的classloader

1, 它有一个工作目录.
2, 它搜索工作目录下的两个子目录classes和classes2,从中加载class. 其中classes优先级高于classes2.

实现方法:
1, 创建两个java project. 一个用来实现classloader. 另一个用来写classloader要加载的classes.
2, 两个项目使用interface Test作为结合.classloader会加载一个Test的实现类,然后将其实例化并调用其test()方法.
3, Main方法了面将启动两个classloader, 分别加载自己的Test类. 它们可以互不干扰的各自工作.每个Test都会调用自己的Printer来打印自己的static field. 可以看到一个现象,虽然两个classloader加载了同样的类,两个类的static field都各自拥有,互不干扰.它们是不共享的.

运行方法:
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,以达到各种测试目的.

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

}





转载于:https://my.oschina.net/xpbug/blog/102368

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值