Tomcat 打破双亲委派机制的目的其实很简单,我们知道 Web 容器可能是需要部署多个应用程序的。但是假设不同的应用程序可能会同时依赖第三方类库的不同版本。我们知道类加载的时候要确保唯一性的,但是总不能要求同一个类库在 Web 容器中只有一份吧?所以 Tomcat 就需要保证每个应用程序的类库都是相互隔离并独立的,这也是它为什么打破双亲委派机制的主要目的。实现 Web 应用程序之间的类加载器相互隔离独立的是 WebAppClassLoader 类加载器。它为什么可以隔离每个 Web 应用程序呢?原因就是它打破了"双亲委派"的机制,如果收到类加载的请求,它会先尝试自己去加载,如果找不到在交给父加载器去加载,这么做的目的就是为了优先加载 Web 应用程序自己定义的类来实现 Web 应用程序相互隔离独立的。
如何打破?继承 ClassLoad 重写 findClass()。
// Tomcat 打破双亲委派机制的伪代码样例
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
public class WebappClassLoader extends URLClassLoader {
// 存储已经加载的类,以避免重复加载
private final Map<String, Class<?>> loadedClasses = new HashMap<>();
// 构造函数,接受类路径的 URL 数组
public WebappClassLoader(URL[] urls) {
super(urls);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 第一步: 检查类是否已经被加载
Class<?> clazz = loadedClasses.get(name);
if (clazz != null) {
return clazz;
}
// 第二步: 尝试使用自定义逻辑加载类
try {
// 在这里可以实现自定义的类加载逻辑
// 为简化示例,这里直接调用父类的方法
clazz = super.findClass(name);
loadedClasses.put(name, clazz); // 将加载的类缓存起来
return clazz;
} catch (ClassNotFoundException e) {
// 第三步: 如果未找到,委托给父类加载器
return super.findClass(name);
}
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
// 自定义资源加载的实现
return super.getResources(name);
}
// 可以根据需要重写更多的方法
}