`

Tomcat 源代码分析之ClassLoader

阅读更多

 

Tomcat 源代码分析之ClassLoader

此系列文章皆为Tomcat 7.0代码代码分析。

 

1. ClassLoader基础知识

1.1. Parent-Child委托模型

 

我们知道Java系统中,类加载器的默认加载方式是采用Parent-Child委托方式加载类的,即就是说,先尝试使用父类加载器加载类,如果没有找到,才自己加载该类,可以看到,这是一个递归的加载过程,核心代码大致如下:

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);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    …
                    c = findClass(name);
                    ……
                }
            }
            ……
            return c;
        }
    }
 很多ClassLoader的继承类都默认了这种加载方式,其中用途比较广泛的有URLClassLoader。

1.2. 类加载器运行时模型:

 

当一个JVM启动时,至少有三个ClassLoader会被启动:

1. Bootstrap 类加载器

加载Java核心包,它们被置放在(<JAVA_HOME>/lib目录下,这部分是JVM的一部分,往往使用native code完成

2. Extensions类加载器

加载Java扩展包,即位于<JAVA_HOME>/lib/ext下的包,这里要注意的是,有些JVM的这个加载器和Bootstrap类加载器是同一个加载器,而Sun是把二者分开的,其实现类为sun.misc.Launcher$ExtClassLoader。

3. System类加载器

这个类加载器加载CLASSPATH下的类,Sun的 默认实现是sun.misc.Launcher$ExtClassLoader。

这三者之间的父子关系是:Bootstrap类加载器是Extensions类加载器的父类加载器,而Extensions类加载器是System类加载器的父类加载器。

2. Tomcat Classloader模型

Tomcat 7.0的ClassLoader加载层次模型如下图所示:

 

 

这个模型和之前Tomcat 5.5之前有些不同,因为之前的除过Common类加载器之外,还有Catalina类加载器和Shared类加载器,这个只能导致更多的配置和概念,已经不再使用了,虽然还可以进行配置。

1. Common类加载器:加载$CATALINA_HOME/lib和$CATALINA_BASE/lib下的class文件和jar包以及旗下的资源文件,这些文件将会被所有Web应用共有,也会被Tomcat运行时用到。其配置在catalina.properties下,如果没有配置,则默认使用System类加载器,配置示例:
common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar
2. App类加载器:Web 应用的类加载器,Web应用下的资源文件,class文件盒jar包,按照Java Web规范的标准,它们位于Web应用的/WEB-INF/classes和/WEB-INF/lib文件夹下。

3. 类加载器的实现

 

Tomcat类加载器其实有三个类实现:StandardClassLoader,WebappClassLoader和JasperLoader,这三个类加载器都是URLClassLoader的子类。不同的是,StandardClassLoader并没有客户化URLClassLoader方法,即,它也是采用委托的方式加载类的。而其他都覆写了loadClass()方法。

1. StandardClassLoader类加载器会创建Common类加载器的实例。

2. WebappClassLoader为每个应用创建类加载器实例,对于加载Web应用的类时,我们并非推荐使用父子委托模型,但是必须保证以java.*和javax.*开头的包必须由System类加载器加载,即最终由Bootstrap加载器加载。

核心代码如下:

public Class loadClass(String name, boolean resolve)
        throws ClassNotFoundException {

       ……

        // (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
       //保证以java.*和javax.*开头的包必须由System类加载器加载
        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
        //Mbeans加载时,是否采用delegate方式
        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) {
            ;
        }

        // 最后使用父加载器加载
        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);
    }

可以看到,并非简单的父子委托方式加载类

3. JasperLoader是为了加载jsp编译成的servlet而创建的类加载器,它覆写了loadClass()方法,除过加载org.apache.jsp.*的文件外,其他的均使用父加载器加载。

核心代码如下:

public Class loadClass(final String name, boolean resolve)
        throws ClassNotFoundException {

        Class clazz = null;                
                                           
        // (0) Check our previously loaded class cache
        clazz = findLoadedClass(name);     
        if (clazz != null) {               
            if (resolve)                   
                resolveClass(clazz);       
            return (clazz);        
        }                          
                          
        // (.5) Permission to access this class when using a SecurityManager
        ……

	if( !name.startsWith(Constants.JSP_PACKAGE_NAME) ) {
            // Class is not in org.apache.jsp, therefore, have our
            // parent load it
            clazz = parent.loadClass(name);            
	    if( resolve )
		resolveClass(clazz);
	    return clazz;
	}
        //所有的以org.apache.jsp.*开头的文件都由该类加载器加载。
	return findClass(name);
    }
 

 

今天就讲到这里了。

 

 

  • 大小: 10.9 KB
分享到:
评论
2 楼 superysy 2012-06-13  
看得云里雾里的
1 楼 c929833623 2012-02-17  
分析得不错,继续关注

相关推荐

    tomcat 类加载机制 —— ClassLoader

    NULL 博文链接:https://yjhexy.iteye.com/blog/668334

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

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

    Tomcat 5.0.18 ClassLoader source code insight

    NULL 博文链接:https://davyjones2010.iteye.com/blog/2226286

    java class加密保护工具

    很容易被反编译工具反编译,而传统的java源代码保护方法基本都是采用混淆的方式, 但这样会带来很多麻烦,而且也不能真正保护class文件, 本工具是对class文件进行加密,采用jni的方式解密运行, 加密算法达到256位...

    java class加密保护(完全免费) v2.1

    很容易被反编译工具反编译,而传统的java源代码保护方法基本都是采用混淆的方式, 但这样会带来很多麻烦,而且也不能真正保护class文件, 本工具是对class文件进行加密,采用jni的方式解密运行, 加密算法达到256位...

    Java类加密2.0版本,无限制

    JAVA CLASS文件加密工具是一款专门为保护您的JAVA源代码而设计的软件。传统的JAVA代码保护方式通常是扰乱生成的CLASS文件,从而降低反编译生成的源代码的可读性;有的保护工具甚至能生成部分废代码(垃圾代码),...

    Java类加密工具v2.2(免注册)

    很容易被反编译工具反编译,而传统的java源代码保护方法基本都是采用混淆的方式, 但这样会带来很多麻烦,而且也不能真正保护class文件, 本工具是对class文件进行加密,采用jni的方式解密运行, 加密算法达到256...

    java类加密工具v2.1

    很容易被反编译工具反编译,而传统的java源代码保护方法基本都是采用混淆的方式, 但这样会带来很多麻烦,而且也不能真正保护class文件, 本工具是对class文件进行加密,采用jni的方式解密运行, 加密算法达到256位...

    我心飞扬java类加密工具2.1(亲测可用)

    众所周知,java编译后的class文件是一种中间字节字文件,很容易被反编译工具反编译,而传统的java源代码保护方法基本都是采用混淆的方式,但这样会带来很多麻烦,而且也不能真正保护class文件,本工具是对class文件...

    Java类加密程序

    简要介绍 JAVA CLASS文件加密工具是一款专门为保护您的JAVA源代码而设计的软件。传统的JAVA代码保护方式通常是扰乱生成的CLASS文件,从而降低反编译生成的源代码的可读性;有的保护工具甚至能生成部分废代码...

    高级java笔试题-itresource:程序开发技术资源集合

    解决eclipse显示jar源代码中文乱码问题 ] () [ 使用 RMI + ZooKeeper 实现远程调用框架 ] () [ Java 注解指导手册 – 终极向导 ] () [ 轻量级分布式 RPC 框架 ] () [ 一个简单的guava cache 例子] () [ 你应该知道的...

    ClassFinal字节码加密工具-其他

    运行加密项目时,无需求修改tomcat,spring等源代码。支持普通jar包、springboot jar包以及普通java web项目编译的war包。支持spring framework、swagger等需要在启动过程中扫描注解或生成字节码的框架。支持maven...

    springboot参考指南

    启用Tomcat的多连接器(Multiple Connectors) viii. 64.8. 在前端代理服务器后使用Tomcat ix. 64.9. 使用Jetty替代Tomcat x. 64.10. 配置Jetty xi. 64.11. 使用Undertow替代Tomcat xii. 64.12. 配置Undertow xiii....

    超级有影响力霸气的Java面试题大全文档

    但通常情况下,由于Java Bean是被容器所创建(如Tomcat)的,所以Java Bean应具有一个无参的构造器,另外,通常Java Bean还要实现Serializable接口用于实现Bean的持久性。Java Bean实际上相当于微软COM模型中的本地...

Global site tag (gtag.js) - Google Analytics