受到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在这个例子里。
现在已经没有什么阻止我们自己做出一个应用服务器了。
分享到:
相关推荐
在Java虚拟机(JVM)中,类加载器(ClassLoader)是至关重要的组成部分,它负责查找和加载类的字节码文件。理解ClassLoader的工作机制对于深入掌握Java应用程序的运行至关重要。这里我们将详细讨论ClassLoader的运行...
这篇教程将对Java ClassLoader进行概述,并指导你构建一个示例ClassLoader,该ClassLoader可以自动编译代码后再加载。学习ClassLoader的工作原理以及如何创建自己的ClassLoader是必要的。 首先,让我们深入了解...
### Java虚拟机中ClassLoader概述与双亲委托机制详解 #### 一、ClassLoader概念与作用 在Java编程语言中,`ClassLoader`是一个非常重要的组件,它负责加载程序运行所需的类文件到Java虚拟机(JVM)中。`ClassLoader`...
在Java中,Classloader是加载类的关键组件,它负责查找、加载和初始化字节码文件。自定义Classloader允许开发者根据特定需求定制类的加载逻辑,例如加密类文件、隔离不同版本的库或者动态加载代码。本文将深入探讨...
在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中。理解ClassLoader的工作原理以及如何定制它,对于深入学习Java的运行机制和进行高级应用开发具有重要意义。本篇文章将...
- Java的类加载机制遵循“双亲委派模型”,即一个类加载请求会先由顶级的Bootstrap ClassLoader处理,如果没有找到,则交给Extension ClassLoader,再未找到则继续交给App ClassLoader,最后是用户自定义的...
当一个类被加载时,它首先会尝试由当前线程的Context ClassLoader进行加载,如果该类加载器无法加载,则向上委托给父类加载器,直至Bootstrap ClassLoader。这种设计可以避免类的重复加载,同时保证核心库的稳定性和...
Java ClassLoader机制是Java虚拟机(JVM)中一个至关重要的组成部分,它的主要任务是将类的.class文件加载到JVM中,使得程序能够运行。ClassLoader不仅负责类的加载,还涉及类的验证、初始化等一系列过程。理解...
《深入理解ClassLoader工作机制》 Java虚拟机(JVM)中的ClassLoader是负责加载类到内存中的核心组件。它不仅承担着将字节码转换为可执行对象的重任,还参与了类生命周期的各个阶段,包括加载、验证、准备、解析、...
首先,ClassLoader可以分为三种基本类型:Bootstrap ClassLoader、Extension ClassLoader和Application ClassLoader。Bootstrap ClassLoader是JVM启动时的第一个ClassLoader,负责加载JDK的`<JAVA_HOME>\lib`目录下...
《ClassLoader详解》 Java应用程序的运行离不开类的加载,而ClassLoader正是这个过程的关键角色。它负责将类的字节码加载到Java虚拟机(JVM)中并转换为可执行的Java对象。深入理解ClassLoader的工作原理对于优化...
Java ClassLoader机制是Java运行时环境中的核心组件之一,它负责加载类到JVM(Java虚拟机)中,使得程序能够执行。理解ClassLoader的工作原理对于优化应用性能、处理类加载问题以及实现自定义加载器至关重要。 首先...
在Java编程语言中,ClassLoader是核心组件之一,它负责加载类到JVM(Java虚拟机)中。自定义ClassLoader允许开发者根据特定需求加载类,比如动态加载或更新类文件,这在某些高级应用场景中非常有用,如插件系统、热...
Java ClassLoader是一个核心的Java运行时组件,负责加载类到Java虚拟机(JVM)中。它是Java平台的独特特性,因为它允许动态加载类,增强了软件的可扩展性和灵活性。这篇博文(虽然链接不可用)可能深入探讨了...
破解java加密的ClassLoader.java,在classloader植入破解代码
在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中执行。这篇测试主要探讨了ClassLoader的工作原理及其在实际应用中的使用。通过阅读给出的博文链接,我们可以深入理解...