tomcat类加载器

本文介绍了JVM的类加载机制,包括引导类、扩展类和系统类加载器的作用,以及双亲委派模型的工作原理,防止安全隐患。接着详细讨论了Tomcat类加载器如何遵循并调整双亲委派模型,特别是WebappClassLoader的loadClass()方法,分析了Tomcat6中不同加载顺序,并给出了如何实现Web应用间共享Jar包的建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1JVM类加载机制

JVM的ClassLoader通过Parent属性定义父子关系,可以形成树状结构。其中引导类、扩展类、系统类三个加载器是JVM内置的。
它们的作用分别是:
1)引导类加载器:使用native代码实现,在rt.jar等包中搜索运行JVM所需的类,例如java.lang等包下的类。
2)扩展类加载器:负责载入标准扩展目录中的类,例如Sun的JVM的扩展目录是/jdk/jre/lib/ext。
3)系统类加载器:默认的类加载器,搜索环境变量CLASSPATH中指明的路径。
在这里插入图片描述

2双亲委派模型

既然类加载器是树形结构,那加载类时就需要定义类到底由当前加载器还是父加载器去搜索加载。
JVM加载模型的工作过程是:
如果一个类加载器收到了类加载的请求,它不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成。
每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的引导类加载器中。
只有父类加载无法完成这个请求时,子类加载器才会尝试自己去加载。
在这里插入图片描述

为什么要让父类加载器优先去加载呢?试想如果子类加载器先加载,那么我们可以写一些与java.lang包中基础类同名的类,
然后再定义一个子类加载器,这样整个应用使用的基础类就都变成我们自己定义的类了。这样就有很大的安全隐患!
所以自己编写类加载器时,如果没有特殊原因,一定要遵守类加载的双亲委派模型。

3Tomcat类加载器

Tomcat基本遵守了JVM的委派模型,但也在自定义的类加载器中做了细微的调整,以适应Tomcat自身的要求。
下面是Tomcat类加载器WebappClassLoader的核心方法loadClass()的源码。
它覆盖了父类URLClassLoader中的方法,改变了默认的类加载顺序。

public synchronized Class loadClass(String name, boolean resolve)

        throws ClassNotFoundException {

 

        Class clazz = null;

 

        // (0) Check our previously loaded local class cache

        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

        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 system class loader, to prevent

        //       the webapp from overriding J2SE classes

        try {

            clazz = system.loadClass(name);

            if (clazz != null) {

                if (resolve)

                    resolveClass(clazz);

                return (clazz);

            }

        } catch (ClassNotFoundException e) {

            // Ignore

        }

 

        boolean delegateLoad = delegate || filter(name);

 

        // (1) Delegate to our parent if requested

        if (delegateLoad) {

            if (log.isDebugEnabled())

                log.debug("  Delegating to parent classloader1 " + parent);

            ClassLoader loader = parent;

            if (loader == null)

                loader = system;

            try {

                clazz = loader.loadClass(name);

                if (clazz != null) {

                    if (log.isDebugEnabled())

                        log.debug("  Loading class from parent");

                    if (resolve)

                        resolveClass(clazz);

                    return (clazz);

                }

            } catch (ClassNotFoundException e) {

                ;

            }

        }

 

        // (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) {

            ;

        }

 

        // (3) Delegate to parent unconditionally

        if (!delegateLoad) {

            if (log.isDebugEnabled())

                log.debug("  Delegating to parent classloader at end: " + parent);

            ClassLoader loader = parent;

            if (loader == null)

                loader = system;

            try {

                clazz = loader.loadClass(name);

                if (clazz != null) {

                    if (log.isDebugEnabled())

                        log.debug("  Loading class from parent");

                    if (resolve)

                        resolveClass(clazz);

                    return (clazz);

                }

            } catch (ClassNotFoundException e) {

                ;

            }

        }

 

        throw new ClassNotFoundException(name);

    }

具体分析一下:首先findLoadedClass0()和findLoadedClass()分别从本地和父类加载器的缓存中查找当前要加载的类是否已经加载过了。
之后为了避免上面提到的安全问题,Tomcat类加载器会将加载请求委派给系统类加载器。接下来根据delegate变量的设置,决定是先由自己加载,
还是先由父类加载器去加载。

这里介绍一下背景,上面的WebappClassLoader是对应一个Web应用的类加载器,其父亲是Tomcat的lib的加载器。所以delegate变量的值,
决定了Tomcat的类加载顺序。

4Tomcat6的加载顺序

所以在Tomcat 6中默认情况下,不是完全按照先Tomcat的lib再Web应用的lib这种顺序去加载类。
Jar包的加载顺序是:
1)JRE中的Java基础包
2)Web应用WEB-INF\lib下的包
3)Tomcat\lib下的包

如果想要在Web应用间共享一些Jar包,则不仅需要将公共包放在Tomcat的lib下,还要删掉Web应用lib中的包,
否则Tomcat启动时还是会优先加载Web应用lib下的包的。

https://blog.youkuaiyun.com/dc_726/article/details/11873343

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值