分享

关于class.forname和contextClassloader

 燮羽 2010-11-06

自从java1.2开始,java的classloader有了阶层体系。

这是我之前说过的,最初的例如rt.jar里面的class,比如Object是由c++写的bootstrapclassloader来载入的。

bootstrapclassloader的孩子extclassloader搜索jre里面的用于存放java扩展jar的目录负责载入其中的class。

然后是appclassloader或者叫做systemclassloader,是搜索CLASSPATH环境变量或者-cp参数里面指定的目录和压缩文件。

任何一个class的载入都需要一个classloader,而且一旦载入就无法改变,并且如果不加指定或者直接使用,都会默认使用caller的classloader来载入用到的class。

而1.2里面classloader阶层迭代体系是,每次需要载入时,首先看父亲能不能载入,也就是说是从根往下找的,在上层找到就不往下找了,这样的机制能够保证安全性。

class.forname为我们java提供了强大的灵活性,我们甚至在构建系统时,即将运行的代码不存在也没有关系。

public class DynamicLoader
{
public static void main(String[] args)
throws Exception
{
Class toRun = Class.forName(args[0]);
String[] newArgs = scrubArgs(args);
Method mainMethod = findMain(toRun);
mainMethod.invoke(null, new Object[] { newArgs });
}
private static String[] scrubArgs(String[] args)
{
String[] toReturn = new String[args.length-1];
for (int i=1; i<args.length; i++)
{
toReturn[i-1] = args[i].toLowerCase();
}
return toReturn;
}
private static Method findMain(Class clazz)
throws Exception
{
Method[] methods = clazz.getMethods();
for (int i=0; i<methods.length; i++)
{
if (methods[i].getName().equals("main"))
return methods[i];
}
return null;
}
}

像这个程序,可以用来运行任何的java程序,例如我们写了一个Echo:
public class Echo
{
public static void main (String args[])
{
for (int i=0; i<args.length; i++)
{
System.out.println("Echo arg"+i+" = "+args[i]);
}
}
}

我们可以
java DynamicLoader Echo ONE TWO THREE

输出:
Echo arg0 = one
Echo arg1 = two
Echo arg2 = three

于是我们将DynamicLoader打包成jar,扔到ext目录,再次运行:
java DynamicLoader Echo ONE TWO THREE
Exception in thread "main" java.lang.ClassNotFoundException: Echo
at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:191)
at java.lang.ClassLoader.loadClass(ClassLoader.java:280)
at java.lang.ClassLoader.loadClass(ClassLoader.java:237)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:124)
at DynamicLoader.main(DynamicLoader.java:8)

怎么回事呢?
明明当前目录下有Echo.class啊?

这就是阶层迭代机制造成的,载入DynamicLoader的classloader是ext,用class.forname默认也会使用extclassloader,往上只有bootstrapclassloader,根本找不到了。

那怎么办呢?

我们只有使用
Thread t = Thread.currentThread();
ClassLoader cl = t.getContextClassLoader();
Class toRun = cl.loadClass(args[0]);

contextClassLoader来做,或者我们直接指定是systemclassloader
Class.forName(args[0],
true,
ClassLoader.getSystemClassLoader());

这里用了三个参数的forName,只有用以上两种办法,改变载入class的classloader,才能找得到。

contextClassloader是和线程Thread挂钩的,一个线程只有一个,并且可以设定安全机制,如果不加以改换,默认就是systemclassloader,而且这个线程创建出来的线程会保留创建者的classloader。

这些在容器实现和企业级服务器方面或者像jndi,rmi等技术上使用得非常多,光是tomcat最深的classloader是第七层,websphere就更不用说了。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多