`
Rambing
  • 浏览: 32001 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Tomcat7源码解读(四) —— 类加载器1

阅读更多

综述:在上一节中分析了Tomcat的执行过程。这一节将分析,Tomcat启动以及请求处理过程中所涉及到的类与对象,是由谁加载的,Tomcat的类加载器的特点。

 

4.1 JAVA的类加载过程

1)类加载load:从字节码二进制文件.class文件将类加载到内存将内存中的class放到运行时数据区的方法区内。类的初始化过程会在堆区建立一个java.lang.Class对象,用来封装该类相关的数据结构。

 

2)  连接:连接又分为以下小步骤

    a) 验证:出于安全性的考虑,验证内存中的字节码是否符合JVM的规范,类的结构规范、语义检查、字节码操作是否合法、这个是为了防止用户自己建立一个非法的XX.class文件就进行工作了,或者是JVM版本冲突的问题,比如在JDK6下面编译通过的class(其中包含注解特性的类),是不能在JDK1.4的JVM下运行的。

    b) 准备:将类的静态变量进行分配内存空间、初始化默认值。(对象还没生成呢,所以这个时候没有实例变量什么事情)

    c)  解析:不同的JVM实现可能选择不同的解析策略。一种做法是递归的把所有依赖的形式引用(只是申明,并不适用类)都进行解析。而另外的做法则可能是只在一个形式引用真正需要的时候(也就是说在所引用类的对象被创建)才进行解析。也就是说如果一个Java类只是被引用了,但是并没有被真正用到,那么这个类有可能就不会被解析。当前的JVM一般都采用了第二种策略。

 

 3) 类的初始化: 将类的静态变量赋予正确的初始值,这个初始值是开发者自己定义时赋予的初始值,而不是默认值。

 

4.2 类加载器的加载模式

Parent First加载模式(也就是在加载的时候会委托给父亲类加载器优先加载的模式)只是JVM的一种默认实现,并不是所有的类加载都需要实现该模式的。

 

Parent Last加载模式,Tomcat的应用类加载器,WebappClassLoader,每一个Context都会尝试优先加载所需要的类,只有在无法加载到的时候才委托给父亲加载器完成加载操作。之所以实现这样一种策略为了在多应用的环境中,例如应用A,B使用了默认的数据库连接实现,而应用C需要定制,那么这样C应用中只要包含该实现就可以实现覆盖默认的实现。

 

4.3 Tomcat7的类加载器的层级结构


Tomcat7运行时类的加载说明:

1)Bootstrap Class Loader是JVM的内核由C实现的,加载了JVM的核心包rt.jar。rt.jar中的所有类执行其class的getClassLoader()方法都将返回null,例如String.class.getClassLoader()。

 

2)Extension Class Loader主要加载了JVM扩展包中相关的jar包。例如运行下列代码将System.out.println(ZipPath.class.getClassLoader());将得到如下的运行结果:sun.misc.Launcher$ExtClassLoader

 

3)System Class Loader加载CLASSPATH相关的类,例如在Tomcat的Bootstrap的main方法中执行System.out.println(Bootstrap.class.getClassLoader());则将得到:sun.misc.Launcher$AppClassLoader

 

4)Common Class Loader,Tomcat7中的CATALINA_HOME/lib下的jar包。注意Tomcat在启动文件中将启动时配置了-classpath "%CATALINA_HOME%\lib\catalina.jar"因此catalina.jar中的类虽然指定使用类加载器Common Class Loader,但是按JVM的委托加载原则System.out.println(Bootstrap.class.getClassLoader());得到的类加载器是:sun.misc.Launcher$AppClassLoader。

 

5)Webapp Class Loader, 主要负责加载Context容器中的所有的类。实际上该加载器提供了参数delegateLoad供用户设定是否使用parent-first加载。默认该值为false,默认用parent-last加载。出于安全性的考虑对于核心类WebappClassLoader是不允许加载的。包括:java.,javax.servlet.jsp.jstl,javax.servlet.,javax.el

 

4.4 类加载器的相关类结构图


 4.5 类加载器的相关源代码

(一)ClassLoader的load方法

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class c = findLoadedClass(name);
            if (c == null) {
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } 
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

1)在该方法中,首先检查是否已经加载了该类,这里有个问题JVM如何判断一个类是否被加载过的?这里涉及到了类的命名空间问题。在JAVA中判断一个类是否相同不仅看类名是否相同,还要看其类加载器是否相同。同一个类可以被不同的类加载器所加载,并且认为是不同的。该问题可以分解下面两个方面看。

a) 单一加载原则:在加载器链中,一个类只会被链中的某一个加载器加载一次。而不会被重复加载。实现类的共享,Tomcat多个应用,如果需要共享一些jar包,那么只需要交给commonClassLoader加载,那么所有的应用就可以共享这些类。

b) 可见性原则:父加载器加载的类,子加载器是可以访问的。而自加载器所加载的类,父加载器无法访问。不同加载器链之间其是相互不可见,无法访问的。实现隔离,Tomcat就是应用该特性,为每一个Context容器创建一个WebappClassLoader类加载器对象,从而实现了应用间的相互隔离。应用间的类是不可见的所以无法相互访问。

 

2) 如果步骤一中无缓存,查看该类父加载器,如果存在那么委托给付加载器。如果没有父加载器那么认为BootstrapClassLoader是其父加载器,委托进行加载。

 

3)如果父加载器无法加载则抛出ClassNotFoundException,调用抽象方法findClass方法。

 

4)此处的resolveClass方法指的是上文类加载过程中连接的第三步操作。resolve该类的形式引用等等。

 

(二)类URLClassLoader的findClass方法

protected Class<?> findClass(final String name)
         throws ClassNotFoundException
    {
        try {
            return AccessController.doPrivileged(
                new PrivilegedExceptionAction<Class>() {
                    public Class run() throws ClassNotFoundException {
                        String path = name.replace('.', '/').concat(".class");
                        Resource res = ucp.getResource(path, false);
                        if (res != null) {
                            try {
                                return defineClass(name, res);
                            } catch (IOException e) {
                                throw new ClassNotFoundException(name, e);
                            }
                        } else {
                            throw new ClassNotFoundException(name);
                        }
                    }
                }, acc);
        } catch (java.security.PrivilegedActionException pae) {
            throw (ClassNotFoundException) pae.getException();
        }
    }

该方法的核心是获取到JAVA类的字节码,然后调用父类的defineClass方法完成类的构造过程。defineClass是由JVM实现的,不允许被覆写,因此用户类文件就必须遵循JVM的文件规范才能被正确的解析。

 

(三)WebappClassLoader重新覆写了ClassLoader的loadClass方法(删除了部分代码)

@Override
public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        Class<?> clazz = null;
		………
        // (0) 当前对象缓存中检查是否已经加载该类
        clazz = findLoadedClass0(name);

        // (0.1) 检查JVM的缓存,是否已经加载过该类
        clazz = findLoadedClass(name);

        // (0.2) 防止加载一些系统相关的类
        try {
            clazz = system.loadClass(name);
            if (clazz != null) {
                if (resolve)
                    resolveClass(clazz);
                return (clazz);
            }
        } catch (ClassNotFoundException e) {}

        boolean delegateLoad = delegate || filter(name);

        // (1) 如果配置了parent-first模式,那么委托给父加载器
        if (delegateLoad) {
            ClassLoader loader = parent;
            if (loader == null) loader = system;
            try {
                clazz = Class.forName(name, false, loader);
                if (clazz != null) {
                    if (resolve) resolveClass(clazz);
                    return (clazz);
                }
            } catch (ClassNotFoundException e) {}
        }

        // (2) 从应用环境中查找类,主要是应用下的/lib目录与classes目录
        try {
            clazz = findClass(name);
            if (clazz != null) {
                if (resolve)
                    resolveClass(clazz);
                return (clazz);
            }
        } catch (ClassNotFoundException e) {}

        // (3) 如果在当前应用下无法找到所需要的类,再委托给父加载器加载(parent-last)
        if (!delegateLoad) {
            ClassLoader loader = parent;
            if (loader == null)
                loader = system;
            try {
                clazz = Class.forName(name, false, loader);
                if (clazz != null) {
                    if (resolve)
                        resolveClass(clazz);
                    return (clazz);
                }
            } catch (ClassNotFoundException e) {}
        }

        throw new ClassNotFoundException(name);

    }

 

 

4.6 总结

本节介绍了JVM的类加载器原理,Tomcat的类加载器结构,类加载器的实现,以及类加载器的部分源代码。重点需要理解的是类加载器的命名空间这一设计特性。所以的其他内容都是服务该特性的,从而实现了类加载的安全性,可见性等。

 

 

 

  • 大小: 22.9 KB
  • 大小: 136.2 KB
分享到:
评论

相关推荐

    tomcat 类加载机制 —— ClassLoader

    1. **Bootstrap ClassLoader**:这是JVM启动时的第一个类加载器,负责加载JDK核心库(rt.jar)中的类。 2. **Extension ClassLoader**:加载JRE扩展目录(java.ext.dirs)下的JAR文件。 3. **System ClassLoader**...

    tomcat7源码下载

    《深入剖析Tomcat7源码》 Tomcat7是一款广泛使用的开源Java Servlet容器,它实现了Java EE中的Web应用服务器标准,尤其是Servlet和JSP规范。源码下载是开发者深入理解其内部工作原理的重要途径,本篇文章将围绕...

    tomcat类加载器

    这个"DevLoader.zip"文件可能包含与Tomcat自定义类加载器相关的资料,特别是名为"DevLoader"的类加载器,这可能是Tomcat为开发者提供的一种特殊加载器。 首先,我们来理解一下类加载器的基本概念。在Java中,类加载...

    tomcat7源码

    《深入剖析Tomcat7源码》 Tomcat7是一款广泛使用的开源Java Servlet容器,它实现了Java EE中的Servlet和JSP规范。源码分析是提升开发者对服务器内部运作机制理解的重要途径,尤其对于Tomcat这样的核心组件,源码的...

    3-7Tomcat中自定义类加载器的使用与源码实现(1).mp4

    3-7Tomcat中自定义类加载器的使用与源码实现(1).mp4

    tomcat源码阅读(一)——环境搭建

    本篇将聚焦于"Tomcat源码阅读(一)——环境搭建",探讨如何搭建一个适合源码学习的开发环境。 首先,我们需要了解Tomcat是什么。Tomcat是一款开源的Java Servlet容器,由Apache软件基金会维护,实现了Java EE中的...

    tomcat源码+文档pdf+源码解析

    源码解析部分则是对Tomcat源码的深度剖析,涵盖了关键类和方法的作用、设计模式的运用以及性能优化技巧。这有助于开发者理解Tomcat内部的工作流程,例如,如何处理HTTP请求的生命周期,以及线程池是如何调度和管理的...

    java类加载器-tomcat中的类加载器

    下面我们将深入探讨Java类加载器以及Tomcat中的类加载器。 在Java中,类加载器主要分为三个层次:Bootstrap ClassLoader、Extension ClassLoader和AppClassLoader。Bootstrap ClassLoader负责加载JDK的核心库,如rt...

    tomcat7源码环境部署

    这里我们讨论的主题是"Tomcat7源码环境部署",这涉及到对Tomcat服务器的深入理解,包括其内部工作原理、源码分析以及如何在本地环境中构建和部署。下面将详细介绍相关知识点。 首先,源码部署意味着你需要从Apache ...

    tomcat7源码环境搭建

    3. **类加载器机制**:Tomcat的类加载器设计独特,理解它如何加载Web应用的类,以及不同Web应用之间的隔离性是重要的一环。 4. **部署和生命周期管理**:了解Tomcat如何解析并部署WAR文件,以及Web应用的启动、停止...

    tomcat8源码

    Tomcat使用自定义的类加载器来加载Web应用中的类,以实现不同应用之间的类隔离。主要有CommonClassLoader、SharedClassLoader和WebappClassLoader,它们按层次加载类。 5. **连接器(Connector)与协议处理器...

    Tomcat7 源码Eclipse工程

    Tomcat7源码还涉及到了类加载机制,它采用了自定义的类加载器,如`CatalinaClassLoader`和`SharedClassLoader`,理解这些类加载器的工作方式有助于解决类冲突和加载顺序问题。 此外,Tomcat的安全管理也是重要一环...

    tomcat工作原理深入详解——HowTomcatWorks中文版.pdf

    tomcat工作原理深入详解——HowTomcatWorks中文版.pdf

    tomcat 7 及其源码

    下面将详细介绍Tomcat 7及其源码的相关知识点。 1. **Tomcat结构与组件**: - **Catalina**:核心组件,负责Servlet容器的主要功能,处理HTTP请求和响应。 - ** Coyote**:处理网络连接器和协议处理器,如处理...

    tomcat6的源码

    7. **连接器(Connector)**:Tomcat支持多种协议,如HTTP/1.1、AJP等。`Coyote`组件负责处理这些协议,其中` CoyoteConnector`是核心部分。 8. **JSP和 Jasper**:Tomcat内置了JSP引擎Jasper,负责编译JSP页面为...

    Tomcat7.0源码,可直接导入eclipse

    源码阅读可以帮助我们理解其内部机制,如请求解析、连接管理、线程池、类加载器等核心功能的实现。 "可直接导入eclipse"这一特点意味着开发者无需额外的配置步骤,只需简单操作即可在Eclipse这样的集成开发环境中...

    我的tomcat7源码手撕过程

    ### Tomcat7源码手撕过程详解 #### Tomcat初始化流程分析 Tomcat是一个流行的Java Servlet容器,用于部署和运行Web应用程序。理解Tomcat的工作原理对于优化应用性能、解决部署问题至关重要。以下是对Tomcat7启动...

    tomcat 源码分析系列文档

    2. "Tomcat的类载入器以及源码分析.doc":详细介绍了Tomcat的类加载器,如WebappClassLoader,它如何根据不同的web应用程序加载类,以及如何避免类冲突。 3. "理解Tomcat的WebappClassLoader(web应用类加载器)一....

Global site tag (gtag.js) - Google Analytics