public class TomcatClassLoaderDemo {
public static void main(String[] args) throws Exception {
URL[] commonUrls = new URL[]{new URL("/path/to/common")};
// Common ClassLoader
URLClassLoader commonClassLoader = new URLClassLoader(commonUrls, null);
URL[] catalinaUrls = new URL[]{new URL("/path/to/catalina")};
// Catalina ClassLoader "/path/to/catalina"
URLClassLoader catalinaClassLoader = new URLClassLoader(catalinaUrls, commonClassLoader);
URL[] sharedUrls = new URL[]{new URL("/path/to/shared")};
// Shared ClassLoader "/path/to/shared"
URLClassLoader sharedClassLoader = new URLClassLoader(sharedUrls, commonClassLoader);
//模拟实现业务:假设在一个大型企业中,有多个Web应用部署在同一Tomcat服务器上。
// 这些应用都使用了如Log4j或SLF4J这样的日志库。为了避免日志配置冲突或确保一致的日志格式,
// 企业希望所有应用都使用相同版本的日志库。这样,它们就可以共享相同的日志配置,比如日志级别、
// 格式化样式、输出目的地等。将这些日志库放在$CATALINA_BASE/lib中,并将delegate设置为true,
// 可以确保所有Web应用都使用这些共享库,而不是各自携带的版本。
// WebAppClassLoader
WebDefineClassLoader webAppClassLoader1 = new WebDefineClassLoader("/path/to/webapp", sharedClassLoader);
webAppClassLoader1.setDelegate(true);
WebDefineClassLoader webAppClassLoader2 = new WebDefineClassLoader("/path/to/webapp", sharedClassLoader);
webAppClassLoader2.setDelegate(true);
// 使用WebAppClassLoader加载类
Class<?> clazz = webAppClassLoader1.loadClass("com.example.MyClass");
System.out.println("Loaded class: " + clazz.getName());
/**
* Delegate=false类加载器执行顺序
* BootStrapClassLoader ExtClassLoader WebAppClassLoader AppClassLoader CommonClassLoader SharedClassLoader
* Delegate=true类加载器执行顺序
* BootStrapClassLoader ExtClassLoader AppClassLoader CommonClassLoader SharedClassLoader WebAppClassLoader
*/
}
}
class WebDefineClassLoader extends ClassLoader {
private boolean delegate = false;
public void setDelegate(boolean delegate) {
this.delegate = delegate;
}
//("webDefineClassLoader",WebDefineClassLoader.class)
Map<String, Class> resourceEntries = new ConcurrentHashMap<String, Class>();
private String baseDir; // 类加载器的加载路径
public WebDefineClassLoader(String baseDir, ClassLoader parent) {
super(parent);
this.baseDir = baseDir;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 这里简化处理,只按文件系统路径加载类文件
byte[] classData = new byte[0];
try {
classData = loadClassData(name);
} catch (Exception e) {
e.printStackTrace();
}
if (classData == null) {
throw new ClassNotFoundException();
}
return defineClass(name, classData, 0, classData.length);
}
private byte[] loadClassData(String name) throws Exception {
// 从文件系统中加载类文件
// 这里需要实现具体的文件读取逻辑,将.class文件读入字节数组
String path = baseDir + name.replace(".", File.separator) + ".class";
FileInputStream fileInputStream = new FileInputStream(path);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int len = 0;
while ((len = fileInputStream.read()) != -1) {
byteArrayOutputStream.write(len);
}
fileInputStream.close();
return byteArrayOutputStream.toByteArray();
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
//1.查找缓存
Class aClass = resourceEntries.get(name);
if (aClass != null) {
return aClass;
}
//2.查找jdk缓存
aClass = findLoadedClass(name);
if (aClass != null) {
return aClass;
}
//3.使用扩展类加载器,避免jdk类加载不到报错,跳过appClassLoader,避免干扰
ClassLoader extClassLoader = getSystemClassLoader().getParent();
aClass = extClassLoader.loadClass(name);
if (aClass != null) {
return aClass;
}
//4.如果delegate=true 委托父类 sharedClassLoader CommonClassLoader AppClassLoader
if (delegate) {
aClass = Class.forName(name, false, getParent());
}
if (aClass != null) {
return aClass;
}
//5.先查找自己
aClass = findClass(name);
if (aClass != null) {
return aClass;
}
//6.delegate=true 默认 委托父类 sharedClassLoader CommonClassLoader AppClassLoader
if (!delegate) {
aClass = Class.forName(name, false, getParent());
}
if (aClass != null) {
return aClass;
}
throw new ClassNotFoundException(name);
}
}
手写模拟tomcat类加载机制
最新推荐文章于 2024-08-06 11:36:45 发布