Tomcat是如何打破JVM类加载的双亲委托机制的?

JVM的类加载使用了双亲委托机制,通过这个机制会把底层的类加载器委托给父加载器去执行(注意:这里的父子类加载器并不是通过继承关系实现的,这是底层的parent指向上一层的类加载器),这样一直委托到BootStrap ClassLoader中,它会执行加载,当它加载不到就会往下一层一层的传递,这个双向的过程就是双亲委托机制的核心。
但是Tomcat打破了这个机制,目的是为了优先加载 Web 应用目录下的类,然后再加载其他目录下的类,这也是 Servlet 规范的推荐做法,打破双亲委派机制实际就是重写了loadClass方法打破了这个类加载的顺序。

一、JVM的双亲委托机制是怎么实现的?
JDK提供了ClassLader抽象类,该类中LaderClass方法,这个方法是检查传入的给是不是加载了,如果加载了直接返回这个类,如果没加载就调用父类加载器加载,递归后父类加载器没有加载,在自己调用findClass方法加载这个类,就是使用了双亲委托机制。

public Class<?> loadClass(String name) {
 // 查找一下这个类是不是已经加载过了
 	Class<?> c = findLoadedClass(name);
 // 如果没有加载过
 	if( c == null ){
 // 先委托给父加载器去加载,注意这是个递归调用
	 if (parent != null) {
		 c = parent.loadClass(name);
 	}else {
 		// 如果父加载器为空,查找 Bootstrap 加载器是
 		c = findBootstrapClassOrNull(name);}
 	}
 // 如果父加载器没加载成功,调用自己的 findClass 去
 	if (c == null) {
		 c = findClass(name);
	 }
 
 	return c;
 }

二、Tomcat的类加载器
Tomcat自定义了加载器WebAppClassLoader打破了 双亲委派机制,WebAppClassLader首先会自己尝试去加载某个类,如果找不到在代理给父类加载器,其目的就是优化加载Web应用自己定义的类。具体实现就是重写了ClassLoader的findClass和loadClass两个方法。
Tomcat的findClass方法并没有加载相关的类,只是从已经加载的类中查找 这个类有没有被加载,具体的加载是在重写的loadClass方法中实现,从上面的对java的讨论可知,重写了loadClass方法就意味着失去了类加载器的父亲委托机制,需要自己来实现父亲委托机制。
在loadClass方法中,先在本地cache检查是不是加载过这个类,然后再检查系统类加载器的cache中是否加载过,然后尝试使用ExtClassLoader类加载器,尝试使用本地目录搜索class并加载,最后尝试系统类加载器(AppClassLoader)都失败就抛出异常。
loadClass决定了加载顺序,findClass方法决定tomcat去哪里找class重写loadClass就可以打破这个机制。

public Class<?> loadClass(String name, boolean resolve)
 synchronized (getClassLoadingLock(name)) {
 Class<?> clazz = null;
 //1. 先在本地 cache 查找该类是否已经加载过
 clazz = findLoadedClass0(name);
 if (clazz != null) {
 if (resolve)
 resolveClass(clazz);
 return clazz;
 }
 //2. 从系统类加载器的 cache 中查找是否加载过
 clazz = findLoadedClass(name);
 if (clazz != null) {
 if (resolve)
 resolveClass(clazz);
 return clazz;
 }
 // 3. 尝试用 ExtClassLoader 类加载器类加载,为什么
 ClassLoader javaseLoader = getJavaseClassLoader
 try {
 clazz = javaseLoader.loadClass(name);
 if (clazz != null) {
 if (resolve)
 resolveClass(clazz);
 return clazz;
 }
 } catch (ClassNotFoundException e) {
 // Ignore
 }
 // 4. 尝试在本地目录搜索 class 并加载
 try {
 clazz = findClass(name);
 if (clazz != null) {
 if (resolve)
 resolveClass(clazz);
 return clazz;
 }
 } catch (ClassNotFoundException e) {
 // Ignore
 }
 // 5. 尝试用系统类加载器 (也就是 AppClassLoader) 
 try {
 clazz = Class.forName(name, false, pare
 if (clazz != null) {
 if (resolve)
 resolveClass(clazz);
 return clazz;
 }
 } catch (ClassNotFoundException e) {
 // Ignore
 }
 }

 //6. 上述过程都加载失败,抛出异常
 throw new ClassNotFoundException(name);
}

Tomcat 的类加载器打破了双亲委托机制,没有一上来就直接委托给父加载器,而是先在
本地目录下加载,为了避免本地目录下的类覆盖 JRE 的核心类,先尝试用 JVM 扩展类加载器 ExtClassLoader 去加载,然后记载本地目录,最后加载系统类加载器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值