深入剖析tomcat的类加载机制


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,但源码注释中写到不推荐在生产环境中使用。

时间: 2024-10-13 11:56:08

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

Tomcat的类加载机制

Tomcat类加载分为四部分(不是步骤):1.BootStrap 启动类加载2.System 系统类加载 CATALINA_HOME/conf3.Common 通用类加载 CATALINA_HOME/lib4.Webapp 应用类加载 WEB-INF/lib和WEB-INF/classes 当Tomcat启动时,步骤如下:1.BootStrap,加载JVM运行基本的类,以及标准扩展类(jre/lib/ext中的类)2.System,加载catalina.bat/catalina.sh中指定位置的

【Tomcat】Tomcat的类加载机制

在Tomcat中主要有以下几种类加载器:(图片来自网络) tomcat启动时,会创建几种类加载器: 1 Bootstrap 引导类加载器 加载JVM启动所需的类,以及标准扩展类,位于jre/lib/ext下. 2 System 系统类加载器 加载tomcat启动的类,比如bootstrap.jar,通常在catalina.bat或者catalina.sh中指定.位于CATALINA_HOME/bin下. 3 Common 通用类加载器 加载tomcat使用以及应用通用的一些类,位于CATALIN

Tomcat类加载机制

说到本篇的tomcat类加载机制,不得不说翻译学习tomcat的初衷. 之前实习的时候学习javaMelody的源码,但是它是一个Maven的项目,与我们自己的web项目整合后无法直接断点调试.后来同事指导,说是直接把java类复制到src下就可以了.很纳闷....为什么会优先加载src下的java文件(编译出的class),而不是jar包中的class呢? 现在了解tomcat的类加载机制,原来一切是这么的简单. 类加载 在JVM中并不是一次性把所有的文件都加载到,而是一步一步的,按照需要来加

Java和Tomcat类加载机制

加载类是运行程序的基础,了解Java和Tomcat的类加载机制对更有效地开发.调试Web应用程序有一定的积极作用.本文简单介绍Java和Tomcat的类加载机制,希望对大家有所帮助. •JDK/JRE文件结构 在安装JDK后,其典型的目录层次如下所示(JDK 1.6.0): 主要的目录和JAR简述如下: •<JAVA_HOME>\bin: 包含在JDK中的开发工具的可执行文件,一般而言,PATH环境变量应包含该目录. •<JAVA_HOME>\lib: 开发工具使用的文件,其中包括

图解Tomcat类加载机制

说到本篇的tomcat类加载机制,不得不说翻译学习tomcat的初衷. 之前实习的时候学习javaMelody的源码,但是它是一个Maven的项目,与我们自己的web项目整合后无法直接断点调试.后来同事指导,说是直接把java类复制到src下就可以了.很纳闷....为什么会优先加载src下的java文件(编译出的class),而不是jar包中的class呢? 现在了解tomcat的类加载机制,原来一切是这么的简单. 类加载 在JVM中并不是一次性把所有的文件都加载到,而是一步一步的,按照需要来加

图解JVM和Tomcat类加载机制

说到本篇的tomcat类加载机制,不得不说翻译学习tomcat的初衷. 之前实习的时候学习javaMelody的源码,但是它是一个Maven的项目,与我们自己的web项目整合后无法直接断点调试.后来同事指导,说是直接把java类复制到src下就可以了.很纳闷....为什么会优先加载src下的java文件(编译出的class),而不是jar包中的class呢? 现在了解tomcat的类加载机制,原来一切是这么的简单. 类加载 在JVM中并不是一次性把所有的文件都加载到,而是一步一步的,按照需要来加

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

本文转载自http://www.cnblogs.com/xing901022/p/4574961.html 说到本篇的tomcat类加载机制,不得不说翻译学习tomcat的初衷. 之前实习的时候学习javaMelody的源码,但是它是一个Maven的项目,与我们自己的web项目整合后无法直接断点调试.后来同事指导,说是直接把java类复制到src下就可以了.很纳闷....为什么会优先加载src下的java文件(编译出的class),而不是jar包中的class呢? 现在了解tomcat的类加载机

Tomcat、Websphere和Jboss类加载机制

http://blog.csdn.net/lshxy320/article/details/6448972 2       Tomcat 类加载机制 Tomcat Server 在启动的时候将构造一个 ClassLoader 树,以保证模块的类库是私有的     Tomcat Server 的 ClassLoader 结构图如下: - Bootstrap - 载入 JVM 自带的类和 /jre/lib/ext/*.jar - System - 载入 /*.class - Common - 载入 

虚拟机类加载机制(3)——线程上下文类加载器

之所以将线程上下文类加载器(Thread Context ClassLoader)单独拿出来写,确实是因为它涉及的东西比较多,既然带有线程两个字,一定也是非常重要的一个东西. 我们首先来回顾一下类加载器的双亲委派模型. 在上一章<虚拟机类加载机制(2)——类加载器>中我们解释了何为类加载器的“双亲委派模型”,知道了双亲委派模型给我们带了一个好处就是Java类随着它的类一起具备了一种带有优先级的层次关系.简单的例子就是Object类在程序的各种类加载环境中都会由启动类加载器来加载,换言之,它无论