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加载的类从新加载就能够了

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