Classloader委托加载模型的反向问题

Classloader委托加载模型的反向问题 英文就是 back door around the classloading delegation scheme

当A类引用到B类,那么加载B类的classloader就是当时A的classloader,下面一个例子

 

public class A {
	public static void main(String[] args) {
	     System.out.println("class A loaded by " + A.class.getClassLoader());
	        //在调用下面的语句是,B的class会被加载,而加载器则是加载A的classloader
		 B b = new B();
		 b.methodB();
	}

}

 

public class B {

	public void methodB() {
	     System.out.println("class B loaded by " + B.class.getClassLoader());
	}

}

    上面打印输出如下:

    class A loaded by sun.misc.Launcher$AppClassLoader@192d342
    class B loaded by sun.misc.Launcher$AppClassLoader@192d342

 

    可是假使我们有3个类,A,B,C,  A和C在classpath下面,B不在classpath下面,而在C:\jdk1.6.0_29\jre\lib\ext 

    \classes下面,上代码

 

public class A {
	public static void main(String[] args) {
		System.out.println("class A loaded by " + A.class.getClassLoader());
		B b = new B();
		b.methodB();
	}
	
}

 

public class B {

	public void methodB() {
		System.out.println("class B loaded by " + B.class.getClassLoader());

		C c = new C();
		c.methodc();
	}

}

 

public class C {

	public void methodc() {
		System.out.println("class C loaded by " + C.class.getClassLoader());
	}

}

    将A和C放入C盘根目录,B放入C:\jdk1.6.0_29\jre\lib\ext\classes

进入cmd,执行结果如下

     C:\>java A
class A loaded by sun.misc.Launcher$AppClassLoader@192d342
class B loaded by sun.misc.Launcher$ExtClassLoader@6b97fd

Exception in thread "main" java.lang.NoClassDefFoundError: C
        at B.methodB(B.java:8)
        at A.main(A.java:6)
Caused by: java.lang.ClassNotFoundException: C
        at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
        at sun.misc.Launcher$ExtClassLoader.findClass(Launcher.java:229)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
        ... 2 more

 

之所以出现A和B都加载了,而C加载不了,是因为委托模型不能反向,因为加载B的classloader是ExtClassLoader, 所以接下来的C也由此classloader加载,按照委托模型,ExtClassLoader会先交由BootStrapClassLoader搜其管辖目录搜索然后由本身在在 C:\jdk1.6.0_29\jre\lib\ext\下搜索,即便如果都搜索不到C,也不会交由AppClassloader加载,因为委托模型只能由当前classloader向上不能向下(而这里的C只能由Appclassloader加载)

 

如何能让B加载到C呢?只能变换classloader

修改后的B代码如下

public class B {

	public void methodB() {
		System.out.println("class B loaded by " + B.class.getClassLoader());
		ClassLoader cl = Thread.currentThread().getContextClassLoader();
		System.out.println("the thread context classloader was " + cl);


		try {
			Class clazzc = Class.forName("C", true, cl); 
			Method m = clazzc.getMethod("methodC", null);
			Object obj = clazzc.newInstance();
			m.invoke(obj, null);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}

   Every thread has a classloader associated with it . If the thread is a main thread then the class  

   loader associated with it a System Class Loader. 具体Thread.currentThread(). getContextClassLoader

   方法参考文档,在这里他会返回AppClassloader

 

同上方法运行A类在cmd,得到

C:\>java A
class A loaded by sun.misc.Launcher$AppClassLoader@192d342
class B loaded by sun.misc.Launcher$ExtClassLoader@6b97fd
the thread context classloader was sun.misc.Launcher$AppClassLoader@192d342
class C loaded by sun.misc.Launcher$AppClassLoader@192d342

 

 

这种需求哪里会用到?

比如java.sql.Drivermanager是rt.jar中的类,而用户classpath下面的具体driver则是classpath下面的类,则需要用到此方法,具体参见DriverManager.getConnection(x,x,x)方法

也就是说核心类库中调用用户代码之前都需要变更classloader

 

下面文章可以参考:

http://hllvm.group.iteye.com/group/topic/38709

http://stackoverflow.com/questions/13307885/could-you-please-explain-a-code-snippet-in-drivermanager-class-related-to-classl/13308729#13308729

http://stackoverflow.com/questions/1771679/difference-between-threads-context-class-loader-and-normal-classloader

http://www.blogjava.net/lihuaxajh/articles/94371.html

你可能感兴趣的:(java,jvm)