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中的方法,改变了默认的类加载顺序。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
    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下的包的。
ps:题外话,如果想要自己指定一个Tomcat\lib和Web应用lib之外的ClassPath,除了修改Tomcat启动脚本外,
可以为不同Web应用的Context指定一个VirtualWebappLoader,但源码注释中写到不推荐在生产环境中使用。

深入剖析tomcat的类加载机制的更多相关文章

  1. Tomcat架构解析(五)-----Tomcat的类加载机制

    类加载器就是根据类的全限定名(例如com.ty.xxx.xxx)来获取此类的二进制字节流的代码模块,从而程序可以自己去获取到相关的类. 一.java中的类加载器   1.类加载器类别 java中的类加 ...

  2. Java中的类加载器以及Tomcat的类加载机制

    在加载阶段,虚拟机需要完成以下三件事情: 1.通过一个类的全限定名来获取其定义的二进制字节流. 2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构. 3.在Java堆中生成一个代表这个类 ...

  3. 【Tomcat】Tomcat的类加载机制

    在Tomcat中主要有以下几种类加载器:(图片来自网络) tomcat启动时,会创建几种类加载器: 1 Bootstrap 引导类加载器 加载JVM启动所需的类,以及标准扩展类,位于jre/lib/e ...

  4. Tomcat的类加载机制

     一个功能健全的Web服务器,要解决如下几个问题: 部署在同一个服务器上的两个Web应用程序使用的Java 类库可以实现相互隔离.不能要求一个类库在一个服务器中只有一份,服务器应当保证两个应用程序的类 ...

  5. 图解Tomcat类加载机制

    说到本篇的tomcat类加载机制,不得不说翻译学习tomcat的初衷. 之前实习的时候学习javaMelody的源码,但是它是一个Maven的项目,与我们自己的web项目整合后无法直接断点调试.后来同 ...

  6. Java和Tomcat类加载机制

    转自:http://blog.csdn.net/codolio/article/details/5027423 加载类是运行程序的基础,了解Java和Tomcat的类加载机制对更有效地开发.调试Web ...

  7. JVM,Tomcat与OSGi类加载机制比较

    首先一个思维导图来看下Tomcat的类加载机制和JVM类加载机制的过程 类加载 在JVM中并不是一次性把所有的文件都加载到,而是一步一步的,按照需要来加载. 比如JVM启动时,会通过不同的类加载器加载 ...

  8. 图解Tomcat类加载机制(阿里面试题)

    Tomcat的类加载机制是违反了双亲委托原则的,对于一些未加载的非基础类(Object,String等),各个web应用自己的类加载器(WebAppClassLoader)会优先加载,加载不到时再交给 ...

  9. 《转载》图解Tomcat类加载机制

    本文转载自http://www.cnblogs.com/xing901022/p/4574961.html 说到本篇的tomcat类加载机制,不得不说翻译学习tomcat的初衷. 之前实习的时候学习j ...

随机推荐

  1. POJ 1127 Jack Straws (计算几何)

    [题目链接] http://poj.org/problem?id=1127 [题目大意] 在二维平面中,给出一些木棍的左右端点,当木棍相交或者间接相交时 我们判断其连通,给出一些询问,问某两个木棍是否 ...

  2. 【二分法】【尺取法】bzoj2348 [Baltic 2011]Plagiarism

    一开始以为死于精度……调了半天发现死于long long…… 一.二分法: #include<cstdio> #include<cstring> #include<alg ...

  3. C#SerialPort实现串口控制继电器

    最近做了一个小系统,麻雀虽小五脏俱全呀,用到各种线程控制,串口控制等技术.其中串口控制最麻烦,因为继电器的响应很快,根据不同的转接口,返回的数据质量是不一样的,所以不能直接wirte,然后马上read ...

  4. Java高级架构师(一)第24节:加入ehcache,把工程加入到Git

    ehcache的maven配置: <!-- ehcache的jar --> <dependency> <groupId>net.sf.ehcache</gro ...

  5. java 的 CopyOnWriteArrayList类

    初识CopyOnWriteArrayList 第一次见到CopyOnWriteArrayList,是在研究JDBC的时候,每一个数据库的Driver都是维护在一个CopyOnWriteArrayLis ...

  6. 本地DNS如何解析公网域名

    DNS服务器图解: 为了服务于公司内部局域网应用,如域.本地网站.论坛.OA.ERP系统等,我们通常搭建本地DNS服务器. 将本地DNS服务器设置为首选DNS,本地系统解析访问是快了. 但内部DNS怎 ...

  7. Scala实现冒泡排序、归并排序和快速排序

    1.冒泡排序 def sort(list: List[Int]): List[Int] = list match { case List() => List() case head :: tai ...

  8. Foundation框架 - NSNumber类

    NSNumber类 NSFormatter #import <Foundation/Foundation.h> int main(int argc, const char * argv[] ...

  9. 【Swift】学习笔记(三)——字符和字符串

    基本了解了变量的创建和基础数据类型,但是在开发中用得最多的还是字符串.那什么是字符串呢? Swift 的String类型表示特定序列的Character(字符) 类型值的集合,它是值类型具有可变性 S ...

  10. B6:命令模式 Command

    将一个请求封装成一个对象,从而可以用不同的请求对象对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作. 与状态模式和责任链模式的区别是,命令模式设定一次,统一执行 使用场景:1.可设计一 ...