`

Tomcat研究之ClassLoader

阅读更多
Tomcat研究之ClassLoader

在研究Tomcat之前,一般是借用现有的UML工具分析Tomcat整体结构,但要分析Tomcat的流程就必须从分析Tomcat的StartUp入手。Tomcat的启动是从解析bat文件开始,bat文件最终调用org.apache.catalina.startup.Bootstrap开始类的加载。
一.Tomcat的ClassLoader:
    TOMCAT自己的类载入器(ClassLoader)
       +---------------------------+
       |         Bootstrap         |
       |             |             |
       |          System           |
       |             |             |
       |          Common           |
       |         /      \          |
       |     Catalina Shared      |
       +---------------------------+
     其中:
     - Bootstrap - 载入JVM自带的类和$JAVA_HOME/jre/lib/ext/*.jar
     - System   
1.载入$CATALINA_HOME/bin/bootstrap.jar 初始化Tomcat,执行Main方法
2. $JAVA_HOME/lib/tools.jar Sun的工具类,包括编译Jsp为Servlet的工具类
   - Common     这个目录下的类虽然对TOMCAT和所有的WEB APP都可见.但是Web App的类不应该
                    放在这个目录下,所有未打包的Class都在$CATALINA_HOME/common/classes下,所
                    有打包的jar都在
                    $CATALINA_HOME/commons/endorsed和$CATALINA_HOME/common/lib下,默认情况会
                    包含以下几个包:
                    1. jndi.jar JNDI API接口,这个包仅在Java1.2时候装入,1.3以后的版本JDK已
                    自动装入.
                    2. naming-common.jar JNDI接口实现类,Tomcat用这些类在内存中使用Context.
                    3. naming-resources.jar JNDI实现,Tomcat用它们定位Web App的静态资源.
                    4. servlet.jar Servlet,Jsp API
                    5. xerces.jar XML解析器,特定的Web App可以在自己的/WEB-INF/lib 中覆盖.
 
   - Catalina 装入Tomcat实现所有接口的类,这些类对Web App是完全不可见的,所有未打包的类在
                    $CATALINA_HOME/server/classes所有jar包在$CATALINA_HOME/server/lib下.一
                    般情况该ClassLoader将Load下面几个包:
                    1. catalina.jar Servlet容器的Tomcat实现包
                    2. jakarta-regexp-X.Y.jar 正则表达式,请求过滤时使用
                    3. servlets-xxxxx.jar Servlet支持包
                    4. tomcat-coyote.jar Tomcat的Coyote连接实现包
                    5. tomcat-jk.jar Web Server绑定包,允许Tomcat绑定Apache等作为Web Server
                    6. tomcat-jk2.jar 功能同上
                    7. tomcat-util.jar Tomcat工具类,可能被一些Connector用到
                    8. tomcat-warp.jar 用于Apache Server包
     - Shared   载入所有WEB APP都可见的类,对TOMCAT不可见. 所有未打包的类在
                    $CATALINA_HOME/shared/classes所有jar包在$CATALINA_HOME /lib下.
                    默认情况包含下面几个包:
                    1. jasper-compiler.jar Jsp编译器,编译Jsp为Servlet
                    2. jasper-runtime.jar Jsp(已编译成Servlet)运行支持包
                    3. naming-factory.jar 支持Web App使用JNDI的封装包
     -WebAppX   Web App ClassLoader,当Web App被部署是该ClassLoader被创建.所有class都在
                    WEB-INF/classes下,所有jar在WEB-INF/lib下.
  
 特别注意WEB APP自己的ClassLoader的实现与众不同:
 它先试图从WEB APP自己的目录里载入,如果失败则请求父ClassLoader的代理
 这样可以让不同的WEB APP之间的类载入互不干扰.另,Tomcat Server使用的是Catalina   
 ClassLoader,一般的Web App使用的是WebApp ClassLoader.
二. org.apache.catalina.startup.Bootstrap
          该类是Tomcat的执行入口点,我们着重分析下面两个方法:
          1. initClassLoaders,创建ClassLoader层次.
           private void initClassLoaders() {
                try {
                          ClassLoaderFactory.setDebug(debug);
                          //创建common ClassLoader,没有父ClassLoader
                          commonLoader = createClassLoader("common", null);
 
                          //创建catalina ClassLoader,父ClassLoader为common
                          catalinaLoader = createClassLoader("server", commonLoader);
 
                          //创建shared ClassLoader, 父ClassLoader为common
                          sharedLoader = createClassLoader("shared", commonLoader);
                     } catch (Throwable t) {
                          log("Class loader creation threw exception", t);
                          System.exit(1);
                     }
             }
          2. createClassLoader,负责具体的创建工作
             在$CATALINA_HOME/conf/catalina.properties中定义了common, server, shared
             ClassLoader载入类的路径及一些包的安全权限.
         
             //common载入类的路径
             common.loader=${catalina.home}/common/classes,
             ${catalina.home}/common/endorsed/*.jar,${catalina.home}/common/lib/*.jar
                //server载入类的路径
                server.loader=${catalina.home}/server/classes,
                               ${catalina.home}/server/lib/*.jar
 
                //shared载入类的路径
                shared.loader=${catalina.base}/shared/classes,
                               ${catalina.base}/shared/lib/*.jar
         
        /**
          *param name:Load Name
          *param parent:父Loader
          *classLoader的资源分三种:
          *1.未打包的classes,一般是一个目录
          *2.打包的jar目录
          *3.网络资源,一般是网上的一个jar包 (Applet经常用到这样的loader)
          */
        private ClassLoader createClassLoader(String name, ClassLoader parent)
                    throws Exception {
 
          //从catalina.properties中取得改Loader的配置信息
         String value = CatalinaProperties.getProperty(name + ".loader");
         if ((value == null) || (value.equals("")))
                    return parent;
         
          //classes目录
     ArrayList unpackedList = new ArrayList();
          //jar目录
      ArrayList packedList = new ArrayList();
          //网络路径指定的包
      ArrayList urlList = new ArrayList();
 
           StringTokenizer tokenizer = new StringTokenizer(value, ",");
            
             //当前Loader该装载的类
             while (tokenizer.hasMoreElements()) {
                    String repository = tokenizer.nextToken();
                    // Check for a JAR URL repository
            try {
                //如果是网络路径追加url
                urlList.add(new URL(repository));
                continue;
            } catch (MalformedURLException e) {
                // Ignore
            }
 
            // 本地路径
            boolean packed = false;
 
           //${catalina.home}
            if (repository.startsWith(CATALINA_HOME_TOKEN)) {
                repository = getCatalinaHome()
                    + repository.substring(CATALINA_HOME_TOKEN.length());
           //${catalina.base}
           } else if (repository.startsWith(CATALINA_BASE_TOKEN)) {
                repository = getCatalinaBase()
                    + repository.substring(CATALINA_BASE_TOKEN.length());
                    }
                    /**经过上述操作,把catalina.properties里的路径替换成绝对路径*/
           
           //如果是jar文件路径
           if (repository.endsWith("*.jar")) {
                packed = true;
                repository = repository.substring
                    (0, repository.length() - "*.jar".length());
            }
            if (packed) {
                packedList.add(new File(repository));
            } else {
                unpackedList.add(new File(repository));
            }
        }
 
        File[] unpacked = (File[]) unpackedList.toArray(new File[0]);
        File[] packed = (File[]) packedList.toArray(new File[0]);
        URL[] urls = (URL[]) urlList.toArray(new URL[0]);
 
        //调用Factory的方法创建ClassLoader
        return ClassLoaderFactory.createClassLoader
            (unpacked, packed, urls, parent);
    }


 

三. ClassLoaderFactory
          ClassLoaderFactory是用于创建ClassLoader的工厂类,这个类比较简单.
         
//参数含义不再说明,参看上面的分析
public static ClassLoader createClassLoader(File unpacked[],
                                                File packed[],
                                                URL urls[],
                                                ClassLoader parent)
        throws Exception {
 
        if (debug >= 1)
            log("Creating new class loader");
 
        // Construct the "class path" for this class loader
        ArrayList list = new ArrayList();
 
        // 通过class目录构造file协议的url,并追加的list
        if (unpacked != null) {
            for (int i = 0; i < unpacked.length; i++) {
                File file = unpacked[i];
                if (!file.exists() || !file.canRead())
                    continue;
                if (debug >= 1)
                    log(" Including directory or JAR "
                        + file.getAbsolutePath());
                URL url = new URL("file", null,
                                  file.getCanonicalPath() + File.separator);
                list.add(url.toString());
            }
        }
 
        //取出所有jar目录里的jar文件,逐一构造url,并追加的list
        if (packed != null) {
            for (int i = 0; i < packed.length; i++) {
                File directory = packed[i];
                if (!directory.isDirectory() || !directory.exists() ||
                    !directory.canRead())
                    continue;
                String filenames[] = directory.list();
                for (int j = 0; j < filenames.length; j++) {
                    String filename = filenames[j].toLowerCase();
                    if (!filename.endsWith(".jar"))
                        continue;
                    File file = new File(directory, filenames[j]);
                    if (debug >= 1)
                        log(" Including jar file " + file.getAbsolutePath());
                    URL url = new URL("file", null,
                                      file.getCanonicalPath());
                    list.add(url.toString());
                }
            }
        }
 
        //追加网络路径的资源
        if (urls != null) {
            for (int i = 0; i < urls.length; i++) {
                list.add(urls[i].toString());
            }
        }
 
        //调用StandardClassLoader创建实际的Loader
        String array[] = (String[]) list.toArray(new String[list.size()]);
        StandardClassLoader classLoader = null;
        if (parent == null)
            classLoader = new StandardClassLoader(array);
        else
            classLoader = new StandardClassLoader(array, parent);
        classLoader.setDelegate(true);
        return (classLoader);
 
   }
四. StandardClassLoader
          StandardClassLoader继承了URLClassLoader, URLClassLoader类具有从硬盘目录装载类,或从本地或远程装载jar文件的能力.这个类也实现了Reloader接口,提供了自动重新装载类的功能.我们主要看这个类以下及个方法:
1.      构造函数StandardClassLoader
   /**
     * @param repositories url数组,见上分析
     * @param parent 父loader
     */
    public StandardClassLoader(String repositories[], ClassLoader parent) {
       //调用父类的构造函数
       //父类的构造函数将用转换后的repositories生成URLClassPath的实例
       //ucp是类成员变量ucp=new URLClassPath(urls);
      // URLClassPath是sun的扩展包,无法继续跟踪
        super(convert(repositories), parent);
        this.parent = parent;
        this.system = getSystemClassLoader();
        securityManager = System.getSecurityManager();
     if (repositories != null) {
            for (int i = 0; i < repositories.length; i++)
                //处理url
                addRepositoryInternal(repositories[i]);
        }
    }
2.      addRepositoryInternal
   /**
     * @param repository 要处理的url
     */
    protected void addRepositoryInternal(String repository) {
 
        URLStreamHandler streamHandler = null;
        String protocol = parseProtocol(repository);
        if (factory != null)
            streamHandler = factory.createURLStreamHandler(protocol);
 
        // 当前url是指向本地或网路的jar文件,验证jar的正确性
        //下面的代码看似无用其实是在验证jar文件的正确性,如果jar文件错误抛异常中止执行.
        if (!repository.endsWith(File.separator) && !repository.endsWith("/")) {
            JarFile jarFile = null;
            try {
                Manifest manifest = null;
 
                //jar协议
                if (repository.startsWith("jar:")) {
                    URL url = new URL(null, repository, streamHandler);
                    JarURLConnection conn =
                        (JarURLConnection) url.openConnection();
                    conn.setAllowUserInteraction(false);
                    conn.setDoInput(true);
                    conn.setDoOutput(false);
                    conn.connect();
                    jarFile = conn.getJarFile();
               
                //file协议
                } else if (repository.startsWith("file://")) {
                    jarFile = new JarFile(repository.substring(7));
                //file
                } else if (repository.startsWith("file:")) {
                    jarFile = new JarFile(repository.substring(5));
 
                //本地路径的jar文件
                } else if (repository.endsWith(".jar")) {
                    URL url = new URL(null, repository, streamHandler);
                    URLConnection conn = url.openConnection();
                    JarInputStream jis =
                        new JarInputStream(conn.getInputStream());
                    manifest = jis.getManifest();
 
                //其他情况均为错误
                } else {
                    throw new IllegalArgumentException
                        ("addRepositoryInternal: Invalid URL '" +
                         repository + "'");
     &
分享到:
评论

相关推荐

    Tomcat研究之ClassLoader.pdf

    ### Tomcat中的ClassLoader详解 #### 一、引言 在深入了解Tomcat的工作原理时,一个重要的组成部分就是其ClassLoader机制。本文旨在深入剖析Tomcat中特有的类加载器(ClassLoader)体系结构,帮助读者理解Tomcat...

    Tomcat 5.0.18 ClassLoader source code insight

    总的来说,深入研究Tomcat 5.0.18的ClassLoader源码,不仅可以提升我们的技术水平,还能让我们更好地应对Java Web开发中的挑战。对于那些想要深入理解Java类加载机制和Tomcat内部工作的开发者,这是一个不可多得的...

    tomcat 类加载机制 —— ClassLoader

    《Tomcat类加载机制——ClassLoader详解》 在Java Web开发中,Tomcat作为最常用的Servlet容器,其类加载机制对于...通过阅读和研究Tomcat源码,我们可以更深入地了解这一机制,从而更好地驾驭这个强大的Web服务器。

    Tomcat源代码学习研究

    - **Classloading**:Tomcat使用定制的ClassLoader加载Web应用的类,遵循“父类加载优先”原则。 6. **连接器与协议处理** - **NIO和Apr**:Tomcat提供了多种连接器实现,如基于Java NIO的 Coyote Connector 和...

    Tomcat:apache-tomcat-6.0.18

    5. **ClassLoader机制**:Tomcat使用自定义的ClassLoader来加载Web应用程序的类,确保不同应用之间的类隔离,防止冲突。 Tomcat 6.0.18版的特性包括: 1. **性能优化**:相对于之前的版本,6.0.18进行了性能调优,...

    tomcat类加载器

    4. Common ClassLoader:在Tomcat中,它是所有Web应用共享的类加载器,用于加载`common.loader`配置项指定的类路径。 5. WebApp ClassLoader:每个Web应用有自己的类加载器,它加载应用的`WEB-INF/classes`和`WEB-...

    tomcat 学习与分析总结资料

    通过研究这个脚本,我们可以了解如何配置JVM参数,如内存分配、堆大小以及设置系统属性,以优化Tomcat的性能。 2. **Tomcat工作原理** Tomcat基于Coyote和Apr(Apache Portable Runtime)处理HTTP请求。Coyote是...

    深入剖析Tomcat 随书 源码

    《深入剖析Tomcat》这本书是Java开发者们探索Tomcat服务器内部机制的重要参考资料,它带领读者逐步揭开Tomcat的...通过学习和研究Tomcat源码,我们可以提升技术水平,解决实际问题,甚至为Tomcat社区贡献自己的力量。

    Tomcat6的源码

    5. **ClassLoader机制**:Tomcat的类加载机制允许每个Web应用拥有自己的类加载器,避免类冲突。理解这部分源码对于理解和解决部署问题至关重要。 6. **Session管理**:Tomcat处理用户会话,包括创建、跟踪和管理...

    apache-tomcat-7.0.81-src 源码免费下载

    10. **网络编程**:Tomcat底层使用NIO(非阻塞I/O)和BIO(阻塞I/O)模型,这在`java/org/apache/tomcat/util/net`目录下可以深入研究。 通过学习和分析这些源码,开发者不仅可以提升对Java Web技术的理解,还可以...

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

    4. 最后,如果所有父类加载器都未能加载,Catalina ClassLoader才加载Tomcat自身的类。 VSD文件可能包含一个流程图,详细描绘了Tomcat类加载器的工作流程和关系,这对于理解类加载机制非常有帮助。通过阅读和分析该...

    apache-tomcat-7.0.40-src

    1. **Servlet和JSP标准实现**:Tomcat是Java Web应用的标准之一,它遵循Java Servlet和JSP规范。在源码中,你可以看到如何实现这些规范,包括请求处理、响应生成、会话管理以及生命周期管理。 2. **Catalina组件**...

    tomcat8源码的maven项目

    3. **学习部署和加载机制**:Tomcat如何加载和管理Web应用程序,包括WAR文件的部署和Classloader的工作原理。 4. **研究线程模型**:Tomcat如何使用线程来处理并发请求,以及线程池的配置和管理。 5. **深入JSP和...

    tomcat 6 源码

    5. **ClassLoader**:Tomcat使用自定义的ClassLoader来加载Web应用的类。这使得不同应用之间能隔离地运行,避免类冲突。 6. **Lifecycle**:所有Tomcat组件都遵循生命周期接口,如`org.apache.catalina.Lifecycle`...

    tomcat-4.1.40-src

    3. **启动流程**:Tomcat的启动过程始于`bin/catalina.sh`或`catalina.bat`,这些脚本会初始化Java环境,并加载`catalina.jar`中的`org.apache.catalina.startup.ClassLoader`,接着加载`Server`对象,初始化`...

    Tomcat7 源码Eclipse工程

    总的来说,通过研究Tomcat7的源码Eclipse工程,不仅可以提升Java Web开发的专业技能,还能深入了解服务器架构设计,这对于优化应用性能、排查故障以及定制化开发具有极大的价值。在实践中,我们可以结合官方文档和...

    apache-tomcat-源码-lib包

    源码中,ClassLoader的实现(例如`org.apache.catalina.loader.WebappClassLoaderBase`)值得深入研究。 5. **部署和配置**:Tomcat支持多种方式部署Web应用,如WAR文件、目录结构或者通过管理接口。源码中,你可以...

    tomcat_src

    在Java Web开发领域,Tomcat无疑是最为流行的开源应用服务器之一。它以其轻量级、高效和易于管理的特性赢得了广大开发者喜爱。"Tomcat_src"这个压缩包中包含的就是Apache Tomcat的源代码,这为我们深入理解其工作...

Global site tag (gtag.js) - Google Analytics