乐趣区

关于java:Tomcat的ClassLoader打破双亲委派源码解读

java 加载类的时候须要应用类加载器,开发人员能够定制类的加载器,比方 tomcat 就扩大了本人的类加载器。这篇文章联合代码钻研一下 jdk 类的加载器和 tomcat 的类加载

jdk 类的加载

先上图温习一下

来看一下 jdk 的 ClassLoader 的代码

 protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {synchronized (getClassLoadingLock(name)) {
            // 本类加载器从缓存外面拿取曾经加载过的 ClassLoader
            Class<?> c = findLoadedClass(name);
            if (c == null) {long t0 = System.nanoTime();
                try {
                    // 父类为 BootStrapClassLoader 的 classLoader 的 parent 变量为空
                    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
                }

                ..........
    }

留神看一下外围代码,实际上 parent 只有不为空就由 parent 来加载,因而当类未加载的时候,总是由下级来优先加载。

tomcat 的类加载

首先来看一下 tomcat 的类加载器,tomcat 写了一个本人的类加载器,看下基类 WebappClassLoaderBase 的实现

@Override
    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {
           ................
            // 从缓存外面取,不影响加载程序,请着重疏忽
            clazz = findLoadedClass0(name);
            ..........

          
            clazz = findLoadedClass(name);
            .........

           /**
     * The bootstrap class loader used to load the JavaSE classes. In some
     * implementations this class loader is always <code>null</code> and in
     * those cases {@link ClassLoader#getParent()} will be called recursively on
     * the system class loader and the last non-null result used.
     */
            // 留神看正文,javaseLoader 是 bootstrap 加载的,所以 javaseLoader 是扩大类加载器
            ClassLoader javaseLoader = getJavaseClassLoader();
           
            
            clazz = javaseLoader.loadClass(name);
            

            boolean delegateLoad = delegate || filter(name, true);
            
            // (2) Search local repositories
            if (log.isDebugEnabled()) {log.debug("Searching local repositories");
            }
            try {
            // 重写了 findClass 办法,从我的项目中的 lib 和 class 里加载
                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 {
//  继承于 SystemClassLoader 该类,所以这个 parent 是 SystemClassLoader,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);
    }

把程序相干外的代码省略了,留神看正文。
因而来梳理一下加载程序
1. 找 ext 加载器,ext 会优先走 BootStrap 加载器,加载 java 外围类库
2.ext 加载不到就去走重写的 findClass,走 web 我的项目的 lib 包和 class 目录,加载我的项目的 class
3. 重写的 findClass 找不到,就去零碎类加载器 SystemClassLoader 来加载。

tomcat 为什么要重写这个 ClassLoader 呢

1. 最重要的就是做隔离性,不同的 web 包引入不同的 jar 包版本,如果重写类的加载程序,对立走 SystemClassloader 加载,那么只会加载一个类在内存,另外一个版本的 class 永远也不会加载进内存。无奈实现 web 我的项目的隔离
2. 能够针对一个利用进行重新部署,一个利用一个加载器,须要重新部署,就把这个 classLoader 加载的类从新加载就能够了

所以看起来突破双亲委派这种大名头的常识,看一下源码就清晰了,养成浏览源码习惯就能够

退出移动版