当前位置: 代码网 > 服务器>服务器>Tomcat > Tomcat Catalina为什么不new出来原理解析

Tomcat Catalina为什么不new出来原理解析

2024年05月19日 Tomcat 我要评论
一、catalina为什么不new出来?掌握了java的类加载器和双亲委派机制,现在我们就可以回答正题上来了,tomcat的类加载器是怎么设计的?1.web容器的特性web容器有其自身的特殊性,所以在

一、catalina为什么不new出来?

掌握了java的类加载器和双亲委派机制,现在我们就可以回答正题上来了,tomcat的类加载器是怎么设计的?

1.web容器的特性

web容器有其自身的特殊性,所以在类加载器这块是不能完全使用jvm的类加载器的双亲委派机制的。在web容器中我们应该要满足如下的特性:

隔离性

部署在同一个web容器上的两个web应用程序所使用的java类库可以实现相互隔离。设想一下,两个web应用,一个使用了spring3.0,另一个使用了新的的5.0,应用服务器使用一个类加载器,web应用将会因为jar包覆盖而无法启动。

灵活性:

web应用之间的类加载器相互独立,那么就能针对一个web应用进行重新部署,此时web应用的类加载器会被重建,而且不会影响其他的web应用。如果采用一个类加载器,类之间的依赖是杂乱复杂的,无法完全移出某个应用的类。

性能:

性能也是一个比较重要的点。部署在同一个web容器上的两个web应用程序所使用的java类库可以互相共享。这个需求也很常见,例如,用户可能有10个使用spring框架的应用程序部署在同一台服务器上,如果把10份spring分别存放在各个应用程序的隔离目录中,将会是很大的资源浪费——这主要倒不是浪费磁盘空间的问题,而是指类库在使用时都要被加载到web容器的内存,如果类库不能共享,虚拟机的方法区就会很容易出现过度膨胀的风险。

2.tomcat类加载器结构

明白了web容器的类加载器有多个,再来看tomcat的类加载器结构。

首先上张图,整体看下tomcat的类加载器:

可以看到在原先的java类加载器基础上,tomcat新增了几个类加载器,包括3个基础类加载器和每个web应用的类加载器,其中3个基础类加载器可在conf/catalina.properties中配置,具体介绍下:

common

以应用类加载器为父类,是tomcat顶层的公用类加载器,其路径由conf/catalina.properties中的common.loader指定,默认指向${catalina.base}/lib下的包。

catalina

以common类加载器为父类,是用于加载tomcat应用服务器的类加载器,其路径由server.loader指定,默认为空,此时tomcat使用common类加载器加载应用服务器。

shared

以common类加载器为父类,是所有web应用的父类加载器,其路径由shared.loader指定,默认为空,此时tomcat使用common类加载器作为web应用的父加载器。

web应用

以shared类加载器为父类,加载/web-inf/classes目录下的未压缩的class和资源文件以及/web-inf/lib目录下的jar包,该类加载器只对当前web应用可见,对其他web应用均不可见。

默认情况下,common、catalina、shared类加载器是同一个,但可以配置3个不同的类加载器,使他们各司其职。

首先,common类加载器复杂加载tomcat应用服务器内部和web应用均可见的类,如servlet规范相关包和一些通用工具包。

其次,catalina类加载器负责只有tomcat应用服务器内部可见的类,这些类对web应用不可见。比如,想实现自己的会话存储方案,而且该方案依赖了一些第三方包,当然是不希望这些包对web应用可见,这时可以配置server.load,创建独立的catalina类加载器。

再次,shared类复杂加载web应用共享类,这些类tomcat服务器不会依赖

3.tomcat源码分析

3.1 catalinclassloader

首先来看看tomcat的类加载器的继承结构

可以看到继承的结构和我们上面所写的类加载器的结构不同。

大家需要注意双亲委派机制并不是通过继承来实现的,而是相互之间组合而形成的。

所以appclassloader没有继承自 extclassloader,webappclassloader也没有继承自appclassloader。

至于common classloader ,shared classloader,catalina classloader则是在启动时初始化的三个不同名字的urlclassloader。

先来看看bootstrap#init()方法。init方法会调用initclassloaders,同样也会将catalina classloader设置到当前线程设置到当前线程,进入initclassloaders来看看。

    private void initclassloaders() {
        try {
            // 创建 commonloader  catalinaloader sharedloader
            commonloader = createclassloader("common", null);
            if (commonloader == null) {
                // no config file, default to this loader - we might be in a 'single' env.
                commonloader = this.getclass().getclassloader();
            }
            // 默认情况下 server.loader 和 shared.loader 都为空则会返回 commonloader 类加载器
            catalinaloader = createclassloader("server", commonloader);
            sharedloader = createclassloader("shared", commonloader);
        } catch (throwable t) {
            handlethrowable(t);
            log.error("class loader creation threw exception", t);
            system.exit(1);
        }
    }

我们可以看到在initclassloaders()方法中完成了commonclassloader, catalinaclassloader,和sharedclassloader的创建,而且进入到createclassloader方法中。

可以看到这三个基础类加载器所加载的资源刚好对应conf/catalina.properties中的common.loader,server.loader,shared.loader

3.2 层次结构

common/catalina/shared classloader的创建好了之后就会维护相互之间的组合关系

其实也就是设置了父加载器

3.3 具体的加载过程

源码比较长,直接进入到 webappclassloaderbase中的 loadclass方法

@override
    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);
            // (0) check our previously loaded local class cache
            // 检查webappclassloader中是否加载过此类
            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
            // 如果第一步没有找到,则继续检查jvm虚拟机中是否加载过该类
            clazz = findloadedclass(name);
            if (clazz != null) {
                if (log.isdebugenabled()) {
                    log.debug("  returning class from cache");
                }
                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
            // 如果前两步都没有找到,则使用系统类加载该类(也就是当前jvm的classpath)。
            // 为了防止覆盖基础类实现,这里会判断class是不是jvmse中的基础类库中类。
            string resourcename = binarynametopath(name, false);
            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 {
                    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);
                    }
                }
            }
            // 检查是否 设置了delegate属性,设置为true,那么就会完全按照jvm的"双亲委托"机制流程加载类。
            boolean delegateload = delegate || filter(name, true);
            // (1) delegate to our parent if requested
            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
            // 若是没有委托,则默认会首次使用webappclassloader来加载类。通过自定义findclass定义处理类加载规则。
            // findclass()会去web-inf/classes 目录下查找类。
            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
            // 若是webappclassloader在/web-inf/classes、/web-inf/lib下还是查找不到class,
            // 那么无条件强制委托给system、common类加载器去查找该类。
            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);
    }

web应用类加载器默认的加载顺序是:

  • (1).先从缓存中加载;
  • (2).如果没有,则从jvm的bootstrap类加载器加载;
  • (3).如果没有,则从当前类加载器加载(按照web-inf/classes、web-inf/lib的顺序);
  • (4).如果没有,则从父类加载器加载,由于父类加载器采用默认的委派模式,所以加载顺序是appclassloader、common、shared。

tomcat提供了delegate属性用于控制是否启用java委派模式,默认false(不启用),当设置为true时,tomcat将使用java的默认委派模式,这时加载顺序如下:

  • (1).先从缓存中加载;
  • (2).如果没有,则从jvm的bootstrap类加载器加载;
  • (3).如果没有,则从父类加载器加载,加载顺序是appclassloader、common、shared。
  • (4).如果没有,则从当前类加载器加载(按照web-inf/classes、web-inf/lib的顺序)

以上就是tomcat catalina为什么不new出来原理解析的详细内容,更多关于tomcat catalina原理的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com