免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4631 | 回复: 3
打印 上一主题 下一主题

再谈ClassLoader(CurrentClassLoader, ParentClassLoader and ContextClassLoader) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-09-28 09:58 |只看该作者 |倒序浏览
受到Tomcat的Catalina的Lifecycle和Container接口的启发,一直想写一个自己的应用服务器,让多个应用在各自的classpath中独立运行,但一直被CurrentClassLoader, ParentClassLoader和ContextClassLoader 所困惑,现在终于弄清楚他们的关系了,希望和大家分享。

我的想法是整个应用服务器被一段类似如下的xml所配置:

  1. <App name="Server">
  2.         <App name="s1" path="...">
  3.                 <App name="s1_1" path="..." />
  4.         </App>
  5.         <App name="s2" path="...">
  6.                 <App name="s2_1" path="..." />
  7.         </App>
  8. </App>
复制代码

每一个App都是一个应用,同时也是一个容器,最顶上的App就是这个应用服务器本身。应用服务器本身和应用是完全平等的,每个外层的容器(应用)递归地负责内层所有容器(应用)的生命周期。

path所对应的目录下应该有两个子目录,分别是lib和classes,这两个目录定义了这个应用的classpath,应用之间互相看不到其他应用的classpath,仅内层应用可以看到外层的。

一个问题是,如何启动一个线程,让这个线程里运行的代码从指定的ClassLoader查找类的定义。

为了解决这个问题,我们来看一些代码。我们用下面这三个方法从一个路径生成一个URLClassLoader:

  1. private static ClassLoader createClassLoader(String path) throws Exception {
  2.                 List<File> jarList = new ArrayList<File>();
  3.                 getClassPath(jarList, new Filter(), new File(path, "lib"));

  4.                 List<URL> urlList = new ArrayList<URL>();
  5.                 urlList.add(new URL("file:///" + path + "/classes/"));

  6.                 for (int i = 0; i < jarList.size(); ++i) {
  7.                         urlList.add(new URL("file:///"
  8.                                         + ((File) jarList.get(i)).getCanonicalPath()));
  9.                 }
  10.                 return new URLClassLoader((URL[]) urlList.toArray(new URL[0]), Thread
  11.                                 .currentThread().getContextClassLoader());
  12.         }

  13.         private static void getClassPath(List<File> list, Filter filter, File f) {
  14.                 if (f.exists() && f.isDirectory()) {
  15.                         File[] ss = f.listFiles(filter);
  16.                         for (int i = 0; i < ss.length; ++i) {
  17.                                 if (ss[i].isFile()) {
  18.                                         list.add(ss[i]);
  19.                                 } else if (ss[i].isDirectory()) {
  20.                                         getClassPath(list, filter, ss[i]);
  21.                                 }
  22.                         }
  23.                 }
  24.         }

  25.         private static class Filter implements FilenameFilter {
  26.                 public boolean accept(File dir, String name) {
  27.                         File f = new File(dir, name);
  28.                         boolean isDir = f.isDirectory();
  29.                         boolean isFile = f.isFile();
  30.                         boolean isJar = name.toLowerCase().endsWith(".jar");
  31.                         boolean isZip = name.toLowerCase().endsWith(".zip");
  32.                         return (isFile && (isJar || isZip)) || isDir;
  33.                 }
  34.         }
复制代码

createClassLoader方法从path目录下的lib和classes两个目录下找到类的定义,并返回对应的URLClassLoader。这是第一步。

接下来我们需要启动一个线程(模拟一个App),让这个线程中运行的代码到createClassLoader返回的ClassLoader的classpath中查找类的定义,并且设置新线程(App)的ParentClassLoader成启动这个线程的线程当前ClassLoader,和设置新线程(App)的ContextClassLoader成createClassLoader返回的ClassLoader。我们先看新线程(App)的代码:

  1. package test;
  2. public class T extends Thread {
  3.         public void run() {
  4.                 System.out.println("App context: "
  5.                                 + Thread.currentThread().getContextClassLoader());
  6.                 System.out.println("App parent: "
  7.                                 + this.getClass().getClassLoader().getParent());
  8.                 System.out.println("App current: " + this.getClass().getClassLoader());

  9.         }
  10. }
复制代码


最后用一段代码调用启动这个测试程序:

  1. public static void main(String[] args) throws Exception {
  2.                 ClassLoader appClassLoader = createClassLoader("D:/P/eclipse/workspace/TestApp");
  3.                 Class c = appClassLoader.loadClass("test.T");
  4.                 Object t = c.newInstance();

  5.                 Method setContextClassLoader = c.getMethod("setContextClassLoader",
  6.                                 new Class[] { ClassLoader.class });
  7.                 setContextClassLoader.invoke(t, appClassLoader);

  8.                 Method start = c.getMethod("start", new Class[] {});
  9.                 start.invoke(t, new Object[] {});
  10.                 System.out.println("Main context: "
  11.                                 + Thread.currentThread().getContextClassLoader());
  12.                 System.out.println("Main current: " + Test.class.getClassLoader());
  13.                 System.out.println("Main parent: "
  14.                                 + Test.class.getClassLoader().getParent());

  15.                 System.out.println("App ClassLoader: " + appClassLoader);
  16.                 System.out.println("Systen ClassLoader: "
  17.                                 + ClassLoader.getSystemClassLoader());
  18.         }
复制代码


输出的结果是:

  1. Main context: sun.misc.Launcher$AppClassLoader@11b86e7
  2. Main current: sun.misc.Launcher$AppClassLoader@11b86e7
  3. Main parent: sun.misc.Launcher$ExtClassLoader@35ce36
  4. App ClassLoader: java.net.URLClassLoader@addbf1
  5. Systen ClassLoader: sun.misc.Launcher$AppClassLoader@11b86e7
  6. App context: java.net.URLClassLoader@addbf1
  7. App parent: sun.misc.Launcher$AppClassLoader@11b86e7
  8. App current: java.net.URLClassLoader@addbf1
复制代码


其中Main代表外层容器,App代表内层容器。我们发现,这个时候App的ContextClassLoader和CurrentClassLoader都已经是我们用createClassLoader方法生成的ClassLoader了,而它的ParentClassLoader是外层容器的CurrentClassLoader,是SystemClassLoader在这个例子里。

现在已经没有什么阻止我们自己做出一个应用服务器了。

论坛徽章:
0
2 [报告]
发表于 2009-11-13 12:27 |只看该作者
http://sourceforge.net/projects/smallcore/
我的想法的第一个实现发布啦,版本号是0.0.1。

论坛徽章:
0
3 [报告]
发表于 2009-11-17 13:30 |只看该作者
没有文档,没有DEMO。。。。
只看源码?

论坛徽章:
0
4 [报告]
发表于 2009-11-17 15:40 |只看该作者
现在有demo和源码,但文档还没有,非常对不起。这个版本是0.0.1只是验证了所有的概念,谢谢qbq的支持。我会努力更新的。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP