Tomcat源码初识三 双亲委派与Tomcat如何打破双亲委派

Tomcat源码初识一 Tomcat整理流程图
Tomcat源码初识二 用文字描述整体流程
Tomcat源码初识三 Tomcat如何实现热加载与热部署
Tomcat源码初识四 Tomcat如何打破双亲委派
Tomcat源码初识五 Tomcat如何处理HTTP请求

java中类加载器加载顺序如下图:
在这里插入图片描述
sun.misc.Launcher类中,初始化了ExtClassLoader 加载器与AppClassLoader加载器

public Launcher() {
    Launcher.ExtClassLoader var1;
    try {
    	//创建ExtClassLoader
        var1 = Launcher.ExtClassLoader.getExtClassLoader();
    } catch (IOException var10) {
        throw new InternalError("Could not create extension class loader", var10);
    }

    try {
    	//创建AppClassLoader
        this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
    } catch (IOException var9) {
        throw new InternalError("Could not create application class loader", var9);
    }
	//设置默认类加载器为app类加载器
    Thread.currentThread().setContextClassLoader(this.loader);
    ...
}

ExtClassLoader类是Launcher类中得到内部类,继承自URLClassLoader
调用ExtClassLoader构造函数时,赋值父加载器为null,(null实际上是启动类加载器)

static class ExtClassLoader extends URLClassLoader {
	//获取ExtClassLoader(Launcher调用此方法创建ExtClassLoader)
    public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
        final File[] var0 = getExtDirs();
        try {
            return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
                public Launcher.ExtClassLoader run() throws IOException {
                    int var1 = var0.length;

                    for(int var2 = 0; var2 < var1; ++var2) {
                        MetaIndex.registerDirectory(var0[var2]);
                    }

                    return new Launcher.ExtClassLoader(var0);
                }
            });
        } catch (PrivilegedActionException var2) {
            throw (IOException)var2.getException();
        }
    }
    //构造函数,创建ExtClassLoader对象,设置父加载器为null(null实际上是启动类加载器)
    public ExtClassLoader(File[] var1) throws IOException {
        super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
        SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
    }
    //默认加载文件路径
    private static File[] getExtDirs() {
            String var0 = System.getProperty("java.ext.dirs");
            File[] var1;
            if (var0 != null) {
                StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
                int var3 = var2.countTokens();
                var1 = new File[var3];
                for(int var4 = 0; var4 < var3; ++var4) {
                    var1[var4] = new File(var2.nextToken());
                }
            } else {
                var1 = new File[0];
            }
            return var1;
        }
	...
}

AppClassLoader 类是Launcher类中得到内部类,继承自URLClassLoader
调用AppClassLoader 构造函数时,赋值父加载器为ExtClassLoader
默认加载文件路径为java.class.path

static class AppClassLoader extends URLClassLoader {
	//获取getAppClassLoader,Launcher调用此方法创建AppClassLoader,var0为ExtClassLoader对象
    public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
    	//默认加载文件路径
        final String var1 = System.getProperty("java.class.path");
        final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
        return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
            public Launcher.AppClassLoader run() {
                URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
                return new Launcher.AppClassLoader(var1x, var0);
            }
        });
    }
	//构造函数,设置父加载器为ExtClassLoader加载器
    AppClassLoader(URL[] var1, ClassLoader var2) {
        super(var1, var2, Launcher.factory);
        this.ucp.initLookupCache(this);
    }
	...
}

AppClassLoader 与ExtClassLoader的继承关系如下图:
在这里插入图片描述
调用loadClass时,会调用父类ClassLoader的loadClass(这里就是双亲委派代码),代码如下:

protected Class<?> loadClass(String name, boolean resolve)  throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 查看类是不是已经加载过了
            Class<?> c = findLoadedClass(name);
            if (c == null) {
            	//如果没有加载过,开始加载
                long t0 = System.nanoTime();
                try {
                	//父加载器不为空,用父加载器加载
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                    	//父加载器为空,用启动类加载器加载
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
				//如果父加载器没有加载到类,用当前类加载器加载
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    //将类的字节码解析成Class类
                    c = findClass(name);
                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
}

经过上面的代码,就能看出来java中类加载的双亲委派是如何实现的,所有类都会先向上试图让最上级父类加载
双亲委派好处:

  1. 沙箱安全机制,底层类库不允许篡改
  2. 父类加载过的类,子类不再加载

Tomcat打破双亲委派:
为什么要打破双亲委派:tomcat中会部署多个项目,每个项目可能会出现相同的类名,或者引用的jar包相同但版本不同,这时候不打破双亲委派,一个类只会加载一次,项目就互相之间有冲突,打破双亲委派,可以实现自己的项目加载自己项目的类,互不影响
原理:自定义类加载器,然后重写loadClass,不先由父类加载

  1. 先检查tomcat缓存中是否已经加载过
  2. 判断jvm缓存中是否已经加载过
  3. 尝试使用ExtClassLoader加载
  4. 如果设置了代理,使用AppClassLoader加载
  5. 使用当前类加载器加载
  6. 如果没有设置代理,尝试使用AppClassLoader加载

WebappClassLoaderBase类的继承关系如下图:
在这里插入图片描述

WebappClassLoaderBase重写了ClassLoader的loadClass方法,实现代码:

public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
   synchronized (getClassLoadingLock(name)) {
	     if (log.isDebugEnabled())
	         log.debug("loadClass(" + name + ", " + resolve + ")");
	     Class<?> clazz = null;
	     // Log access to stopped class loader
	     checkStateForClassLoading(name);
	     // tomcat自己做的缓存,查看类是否加载过了
	     clazz = findLoadedClass0(name);
	     if (clazz != null) {
	         if (log.isDebugEnabled())
	             log.debug("  Returning class from cache");
	         if (resolve)
	             resolveClass(clazz);
	         return clazz;
	     }
	     // 调用jvm的缓存,判断类是否加载过
	     clazz = findLoadedClass(name);
	     if (clazz != null) {
	         if (log.isDebugEnabled())
	             log.debug("  Returning class from cache");
	         if (resolve)
	             resolveClass(clazz);
	         return clazz;
	     }
	     String resourceName = binaryNameToPath(name, false);
		 //获取extClassLoader
	     ClassLoader javaseLoader = getJavaseClassLoader();
	     ...
	     if (tryLoadingFromJavaseLoader) {
	         try {
	         	 //尝试使用extClassLoader加载类
	             clazz = javaseLoader.loadClass(name);
	             if (clazz != null) {
	                 if (resolve)
	                     resolveClass(clazz);
	                 return clazz;
	             }
	         } catch (ClassNotFoundException e) {
	             // Ignore
	         }
	     }
		
	   	 ...
	   	 	
	     // (1) Delegate to our parent if requested
	     if (delegateLoad) {
	         if (log.isDebugEnabled())
	             log.debug("  Delegating to parent classloader1 " + parent);
	         try {
	         	 //如果设置了代理(默认没设置),使用appClassLoader加载
	             clazz = Class.forName(name, false, parent);
	             if (clazz != null) {
	                 if (log.isDebugEnabled())
	                     log.debug("  Loading class from parent");
	                 if (resolve)
	                     resolveClass(clazz);
	                 return clazz;
	             }
	         } catch (ClassNotFoundException e) {
	             // Ignore
	         }
	     }
	
	     // (2) Search local repositories
	     if (log.isDebugEnabled())
	         log.debug("  Searching local repositories");
	     try {
	     	 //当前类加载器加载
	         clazz = findClass(name);
	         if (clazz != null) {
	             if (log.isDebugEnabled())
	                 log.debug("  Loading class from local repository");
	             if (resolve)
	                 resolveClass(clazz);
	             return clazz;
	         }
	     } catch (ClassNotFoundException e) {
	         // Ignore
	     }
	
	     // (3) Delegate to parent unconditionally
	     if (!delegateLoad) {
	         if (log.isDebugEnabled())
	             log.debug("  Delegating to parent classloader at end: " + parent);
	         try {
	         	 //如果没设置代理,并且当前类加载器没有加载到,尝试使用appClassLoader类加载器加载
	             clazz = Class.forName(name, false, parent);
	             if (clazz != null) {
	                 if (log.isDebugEnabled())
	                     log.debug("  Loading class from parent");
	                 if (resolve)
	                     resolveClass(clazz);
	                 return clazz;
	             }
	         } catch (ClassNotFoundException e) {
	             // Ignore
	         }
	     }
	 }
	
	 throw new ClassNotFoundException(name);
}

Tomcat源码初识一 Tomcat整理流程图
Tomcat源码初识二 用文字描述整体流程
Tomcat源码初识三 Tomcat如何实现热加载与热部署
Tomcat源码初识四 Tomcat如何打破双亲委派
Tomcat源码初识五 Tomcat如何处理HTTP请求

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值