前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >tomcat类加载-源码解析

tomcat类加载-源码解析

作者头像
逍遥壮士
发布2022-12-01 15:41:58
3980
发布2022-12-01 15:41:58
举报
文章被收录于专栏:技术趋势技术趋势

上文:tomcat热加载、热部署-源码解析


背景

继上文,那么你可能跟我开始一样,tomcat的类加载与我们的java有什么区别?是一样的还是有哪些区别?其次tomcat项目怎么隔离?其三tomat如何打破双亲委派机制?

相关基础

java类如何加载的?

这个问题建议阅读我以往的文章,有这个基础会更好了解如下。文章:

‍类的加载时机

jvm的类加载器(classloader)及类的加载过程

以前画的图,比较丑,别介意哈~

打破双亲委派机制

那么如何打破双亲委派机制,可以参考另一个文章:如何打破双亲委派机制?

看到这里你很明显清楚的知道tomcat其实就基于jdk的类加载机制进行重写Classloader,然后通过classloader进行重写findClass实现的。

tomcat为什么要打破双亲委派机制?

项目之间互相隔离:不同的项目,相同的路径起到互相隔离,方便多部署项目,保证项目之间互不影响(由于一个tomcat可能需要部署多套系统需要);

共享java类库:不同的项目之间可以共享java的类库,不需要重装安装;

支持热部署:新项目加载或部署不会影响正在运行的项目;

tomcat打破双亲委派机制的实现源码

代码位置:org.apache.catalina.loader.WebappClassLoaderBase#loadClass(java.lang.String, boolean)

代码语言:javascript
复制
@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    //同步锁 这里是调用ClassLoader的方法获取
    synchronized (getClassLoadingLock(name)) {
        if (log.isDebugEnabled()) {
            log.debug("loadClass(" + name + ", " + resolve + ")");
        }
        Class<?> clazz = null;

        // Log access to stopped class loader 检查当前的classloader是事已经停止,没有就跑出异常
        checkStateForClassLoading(name);

        // (0) Check our previously loaded local class cache
        // 本地检查缓存中是否存在加载过了。这是指的是webappcloassloader
        clazz = findLoadedClass0(name);
        if (clazz != null) {
            if (log.isDebugEnabled()) {
                log.debug(" Returning class from cache");
            }
            if (resolve) {
                resolveClass(clazz);
            }
            return clazz;
        }

        // (0.1) Check our previously loaded class cache
        //从系统类加载器里面判断是否加载过了,如果是则进行返回 SystemClassLoader
        clazz = findLoadedClass(name);
        if (clazz != null) {
            if (log.isDebugEnabled()) {
                log.debug(" Returning class from cache");
            }
            //如果为直,调用classloader本地方法进行加载类;
            if (resolve) {
                resolveClass(clazz);
            }
            return clazz;
        }

        // (0.2) Try loading the class with the bootstrap class loader, to prevent
        // the webapp from overriding Java SE classes. This implements
        // SRV.10.7.2
        //用于防止系统的类覆盖到jdk默认的lib
        String resourceName = binaryNameToPath(name, false);
        //通过ExtClassloader 查看是否被加载过了
        ClassLoader javaseLoader = getJavaseClassLoader();
        boolean tryLoadingFromJavaseLoader;
        try {
            // Use getResource as it won't trigger an expensive
            // ClassNotFoundException if the resource is not available from
            // the Java SE class loader. However (see
            // https://bz.apache.org/bugzilla/show_bug.cgi?id=58125 for
            // details) when running under a security manager in rare cases
            // this call may trigger a ClassCircularityError.
            // See https://bz.apache.org/bugzilla/show_bug.cgi?id=61424 for
            // details of how this may trigger a StackOverflowError
            // Given these reported errors, catch Throwable to ensure any
            // other edge cases are also caught
            URL url;
            if (securityManager != null) {
                PrivilegedAction<URL> dp = new PrivilegedJavaseGetResource(resourceName);
                url = AccessController.doPrivileged(dp);
            } else {
                url = javaseLoader.getResource(resourceName);
            }
            tryLoadingFromJavaseLoader = (url != null);
        } catch (Throwable t) {
            // Swallow all exceptions apart from those that must be re-thrown
            ExceptionUtils.handleThrowable(t);
            // The getResource() trick won't work for this class. We have to
            // try loading it directly and accept that we might get a
            // ClassNotFoundException.
            tryLoadingFromJavaseLoader = true;
        }
        //默认返回是成功,因为检查一般是通过,不通过会抛出异常
        if (tryLoadingFromJavaseLoader) {
            try {
                //通过Classloader进行加载
                clazz = javaseLoader.loadClass(name);
                //不为空,进行解析并加载到缓存中后返回
                if (clazz != null) {
                    if (resolve) {
                        resolveClass(clazz);
                    }
                    return clazz;
                }
            } catch (ClassNotFoundException e) {
                // Ignore
            }
        }

        // (0.5) Permission to access this class when using a SecurityManager
        //安全检查 主要指包路劲
        if (securityManager != null) {
            int i = name.lastIndexOf('.');
            if (i >= 0) {
                try {
                    securityManager.checkPackageAccess(name.substring(0,i));
                } catch (SecurityException se) {
                    String error = sm.getString("webappClassLoader.restrictedPackage", name);
                    log.info(error, se);
                    throw new ClassNotFoundException(error, se);
                }
            }
        }
        //判断是否使用委托方式(父类加载)
        boolean delegateLoad = delegate || filter(name, true);

        // (1) Delegate to our parent if requested
        //delegateLoad用于判断是否委托加载
        if (delegateLoad) {
            if (log.isDebugEnabled()) {
                log.debug(" Delegating to parent classloader1 " + parent);
            }
            try {
                //通过父类加载
                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 {
            //这里是核心的重写方法,因为这个是重写了classloader进行实现的。(webapp)
            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 {
                //通过父类加载
                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);
}

最后

通过了解java的类加载机制再来学习tomcat的类加载机制,特别是阅读过源码后你会发现非常简单,只是针对Classloader进行重新,根据自已的需求进行判断路劲是通过双亲委派机制进行加载,还是通过自定类加载器进行加载,所以这块建议学习的同学通过源码来学习会快很多,当然在学习之前特别建议把相关的基础一定要扎实。

参考文章:

https://www.jianshu.com/p/9b2d43c9a09a

https://blog.51cto.com/tiantianzaixian/5621528

视频:https://www.bilibili.com/video/BV1o34y197fz?p=8&vd_source=7d0e42b081e08cb3cefaea55cc1fa8b7

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-09-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 技术趋势 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档