- 论坛徽章:
- 0
|
受到Tomcat的Catalina的Lifecycle和Container接口的启发,一直想写一个自己的应用服务器,让多个应用在各自的classpath中独立运行,但一直被CurrentClassLoader, ParentClassLoader和ContextClassLoader 所困惑,现在终于弄清楚他们的关系了,希望和大家分享。
我的想法是整个应用服务器被一段类似如下的xml所配置:
- <App name="Server">
- <App name="s1" path="...">
- <App name="s1_1" path="..." />
- </App>
- <App name="s2" path="...">
- <App name="s2_1" path="..." />
- </App>
- </App>
复制代码
每一个App都是一个应用,同时也是一个容器,最顶上的App就是这个应用服务器本身。应用服务器本身和应用是完全平等的,每个外层的容器(应用)递归地负责内层所有容器(应用)的生命周期。
path所对应的目录下应该有两个子目录,分别是lib和classes,这两个目录定义了这个应用的classpath,应用之间互相看不到其他应用的classpath,仅内层应用可以看到外层的。
一个问题是,如何启动一个线程,让这个线程里运行的代码从指定的ClassLoader查找类的定义。
为了解决这个问题,我们来看一些代码。我们用下面这三个方法从一个路径生成一个URLClassLoader:
- private static ClassLoader createClassLoader(String path) throws Exception {
- List<File> jarList = new ArrayList<File>();
- getClassPath(jarList, new Filter(), new File(path, "lib"));
- List<URL> urlList = new ArrayList<URL>();
- urlList.add(new URL("file:///" + path + "/classes/"));
- for (int i = 0; i < jarList.size(); ++i) {
- urlList.add(new URL("file:///"
- + ((File) jarList.get(i)).getCanonicalPath()));
- }
- return new URLClassLoader((URL[]) urlList.toArray(new URL[0]), Thread
- .currentThread().getContextClassLoader());
- }
- private static void getClassPath(List<File> list, Filter filter, File f) {
- if (f.exists() && f.isDirectory()) {
- File[] ss = f.listFiles(filter);
- for (int i = 0; i < ss.length; ++i) {
- if (ss[i].isFile()) {
- list.add(ss[i]);
- } else if (ss[i].isDirectory()) {
- getClassPath(list, filter, ss[i]);
- }
- }
- }
- }
- private static class Filter implements FilenameFilter {
- public boolean accept(File dir, String name) {
- File f = new File(dir, name);
- boolean isDir = f.isDirectory();
- boolean isFile = f.isFile();
- boolean isJar = name.toLowerCase().endsWith(".jar");
- boolean isZip = name.toLowerCase().endsWith(".zip");
- return (isFile && (isJar || isZip)) || isDir;
- }
- }
复制代码
createClassLoader方法从path目录下的lib和classes两个目录下找到类的定义,并返回对应的URLClassLoader。这是第一步。
接下来我们需要启动一个线程(模拟一个App),让这个线程中运行的代码到createClassLoader返回的ClassLoader的classpath中查找类的定义,并且设置新线程(App)的ParentClassLoader成启动这个线程的线程当前ClassLoader,和设置新线程(App)的ContextClassLoader成createClassLoader返回的ClassLoader。我们先看新线程(App)的代码:
- package test;
- public class T extends Thread {
- public void run() {
- System.out.println("App context: "
- + Thread.currentThread().getContextClassLoader());
- System.out.println("App parent: "
- + this.getClass().getClassLoader().getParent());
- System.out.println("App current: " + this.getClass().getClassLoader());
- }
- }
复制代码
最后用一段代码调用启动这个测试程序:
- public static void main(String[] args) throws Exception {
- ClassLoader appClassLoader = createClassLoader("D:/P/eclipse/workspace/TestApp");
- Class c = appClassLoader.loadClass("test.T");
- Object t = c.newInstance();
- Method setContextClassLoader = c.getMethod("setContextClassLoader",
- new Class[] { ClassLoader.class });
- setContextClassLoader.invoke(t, appClassLoader);
- Method start = c.getMethod("start", new Class[] {});
- start.invoke(t, new Object[] {});
- System.out.println("Main context: "
- + Thread.currentThread().getContextClassLoader());
- System.out.println("Main current: " + Test.class.getClassLoader());
- System.out.println("Main parent: "
- + Test.class.getClassLoader().getParent());
- System.out.println("App ClassLoader: " + appClassLoader);
- System.out.println("Systen ClassLoader: "
- + ClassLoader.getSystemClassLoader());
- }
复制代码
输出的结果是:
- Main context: sun.misc.Launcher$AppClassLoader@11b86e7
- Main current: sun.misc.Launcher$AppClassLoader@11b86e7
- Main parent: sun.misc.Launcher$ExtClassLoader@35ce36
- App ClassLoader: java.net.URLClassLoader@addbf1
- Systen ClassLoader: sun.misc.Launcher$AppClassLoader@11b86e7
- App context: java.net.URLClassLoader@addbf1
- App parent: sun.misc.Launcher$AppClassLoader@11b86e7
- App current: java.net.URLClassLoader@addbf1
复制代码
其中Main代表外层容器,App代表内层容器。我们发现,这个时候App的ContextClassLoader和CurrentClassLoader都已经是我们用createClassLoader方法生成的ClassLoader了,而它的ParentClassLoader是外层容器的CurrentClassLoader,是SystemClassLoader在这个例子里。
现在已经没有什么阻止我们自己做出一个应用服务器了。 |
|