破坏双亲委派模型和自定义自己的类加载器

ClassLoader loadeClass源码:

protected Class loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

1 . 首先检查,这个类是否已经被加载过了

2.  没有被加载过,如果父类加载存在,首先调用父类的加载器加载,没有父类加载使用Bootstrap ClassLoader加载

3. 如果仍然没有成功加载,调用findClass方法(这个方法可以被子类加载器重写),

从源码看出findClass方法是需要开发者自己去实现的,到这里其实就是调用实现的类加载器加载

protected Class findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

从这里看出,双亲委派模型的实现依赖于loadClass方法:

1. 如果不想不破坏双亲委派模型,只要去重写findClass方法

2. 如果想要去破坏双亲委派模型,需要去重写loadClass方法

自定义类加载器

1. 不破坏双亲委派模型,重写findClass方法

public class ProtectedClassLoader extends ClassLoader {
	public  ProtectedClassLoader(ClassLoader parent){
		super(parent);
	}
	@Override
	protected Class findClass(String name) throws ClassNotFoundException {
		byte[] by = null;
		try {
			by = getByteByClassName(name);
			return defineClass(name, by, 0, by.length);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return super.findClass(name);
	}
	@Override
	public InputStream getResourceAsStream(String name) {
		return super.getResourceAsStream(name);
	}
	private byte[] getByteByClassName(String name) throws IOException {
		File file = new File("D:\\MyTest.class");
		InputStream is = new FileInputStream(file);
		byte[] by = new byte[is.available()];
		is.read(by);
		is.close();
		return by;
	}
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException, IOException {
		ClassLoader ml = new ProtectedClassLoader(ClassLoader.getSystemClassLoader().getParent());
		Object obj = Class.forName("com.gold.aip.test.MyTest", true, ml).newInstance();
		System.out.println(obj);
		System.out.println(obj.getClass().getClassLoader());
	}
}

输出结果:

com.gold.aip.test.MyTest@b03be0
com.gold.aip.test.ProtectedClassLoader@14e8cee

测试这段代码的过程出现了两个问题:

1. 如果直接调用自定义类加载器的loadClass,结果还是ApplicationClassLoader,改用Class.forName指定加载器去加载

2. Myeclipse环境下,保存这个类,会自动编译,结果还是使用ApplicationClassLoader,解决方案:

*  删除classpath下编译的class文件

* 指定类加载器的父类为ExtensionClassLoader,这样父加载器无法加载,自然给子类加载加载。

* 因为指定了父类加载器,使用getResourceAsStream方法,从源码看出是调用父类加载器的这个方法,取得的字节流是空的

改用FileInputStream直接读取

2. 破坏双亲加载模型自定义类加载器

package cn.erong.test;

import java.io.InputStream;

public class Test {
	public static void main(String[] args) throws Exception {
		ClassLoader myloader = new ClassLoader() {
			@Override
			public Class loadClass(String name)
					throws ClassNotFoundException {
				String fileName = name.substring(name.lastIndexOf(".")+1)+".class";
				InputStream is;
					try {
						is =getClass().getResourceAsStream(fileName);
						if(is==null) {return super.loadClass(fileName);}
						byte[] by = new byte[is.available()];
						is.read(by);
						is.close();
						return defineClass(name, by, 0, by.length);
					} catch (Exception e) {
						e.printStackTrace();
					}
				return null;
			}
		};
		myloader.loadClass("cn.erong.test.Test").newInstance();
	}
}

这段代码是有问题的,抛出异常:

java.lang.ClassNotFoundException: Object.class

不过能了解到一些东西:

* 测试类Test,继承Object,因为破坏了双亲加载模型,Object类也会使用这个加载器加载,从Classpath下找这个类,必然会出错。

* 断点测试,Object类是后加载的,因为父类永远比子类先初始化,所以可以看出父类是后加载,但是先解析,初始化


你可能感兴趣的:(JVM)