精华内容
下载资源
问答
  • tomcat源码阅读

    2021-02-26 17:34:24
    tomcat源码的时候,我建议和官方的User Guide一起阅读,明白tomcat做某件事情的目的之后,看源码会容易一点。另外,debug当然是一个非常重要的工具。源码上了规模之后,如果单纯静态地看,基本是看不出什么来的...

    读tomcat的源码的时候,我建议和官方的User Guide一起阅读,明白tomcat做某件事情的目的之后,看源码会容易一点。另外,debug当然是一个非常重要的工具。源码上了规模之后,如果单纯静态地看,基本是看不出什么来的,但是跟着数据流走一走,很多问题就清楚了

    debug环境的搭建方法,请看另外一篇博客:http://zhh2009.iteye.com/blog/1557891。这篇文章写得很清楚了,但是我不太明白为什么需要转换成maven工程,以及为什么需要一个dist版本

    作为本系列的第一篇文章,本文不涉及源码,首先介绍一下tomcat的classloader机制

    参考的文档包括:

    http://tomcat.apache.org/tomcat-7.0-doc/class-loader-howto.html

    servlet-spec-2.4-fr

    一、过时的模型

    在网上搜索“tomcat classloader”,很容易搜索到下图,但是这是一个过时的模型

    7ac4f2e57b808e42c7e36336a621c078.png

    这个模型是在tomcat5.x使用的,可以看一下tomcat5.x的目录结构

    a2d0c6fa01de7195a6ea6aa2fd7f69b7.png

    再对比一下tomcat7.x的目录结构

    9d5cc5b21176dbae0c0353c97769b619.png

    可以看到,5.x里的server、shared、common目录,在7.x中已经废弃了。所以上图中的ClassLoader模型也是过时的

    在tomcat7.x里,ClassLoader的模型应该是下图这样:

    bf6d6455b542e3dd945efda33b3faec6.png

    至于这个模型中,各个ClassLoader的具体作用,下文会说明

    二、JVM默认的classloader机制

    jvm默认定义了三种classloader,分别是bootstrap classloader、extension classloader、system classloader

    bootstrap是jvm的一部分,用C写的,每一个java程序都会启动它,去加载%JAVA_HOME%/jre/lib/rt.jar

    extension也差不多,它会去加载%JAVA_HOME%/jre/lib/ext/下的类

    system则是会去加载系统变量CLASSPATH下的所有类

    这3个部分,在上面的tomcat classloader模型图中都有体现。不过可以看到extension没有画出来,可以理解为是跟bootstrap合并了,都是去%JAVA_HOME%/jre/lib下面加载类

    另外,java的classloader一般是采用委托机制,即classloader都有一个parent classloader,当它收到一个加载类的请求时,会首先请求parent classloader加载,如果parent classloader加载不到,才会自己去尝试加载(如果自己也加载不到,则抛出ClassNotFoundException)。采用这种机制的目的,主要是从安全角度考虑。比如用户自己定义了一个java.lang.Object,把jdk中的覆盖了,那显然是有问题的

    当然,这个机制不是绝对的,比如在OSGi中,就故意违反了这个模式。后面可以看到,tomcat里的webapp classloader也违反了这个规定

    三、tomcat为什么要自定义classloader

    主要有2个目的,首先是要实现servlet规范中对类加载的要求,其次是实现不同web app的类隔离

    servlet规范中对类加载要求如下:

    This specification defines a hierarchical structure used for deployment and

    packaging purposes that can exist in an open file system, in an archive file, or in some other form. It is recommended, but not required, that servlet containers

    support this structure as a runtime representation.

    Web applications can be packaged and signed into a Web ARchive format (WAR)

    file using the standard Java archive tools. For example, an application for issue

    tracking might be distributed in an archive file called issuetrack.war.

    When packaged into such a form, a META-INF directory will be present which

    contains information useful to Java archive tools. This directory must not be

    directly served as content by the container in response to a Web client’s request, though its contents are visible to servlet code via the getResource and getResourceAsStream calls on the ServletContext. Also, any requests to access the

    resources in META-INF directory must be returned with a SC_NOT_FOUND(404)

    response.

    四、各classloader详细说明

    4.1 Bootstrap — This class loader contains the basic runtime classes provided by the Java Virtual Machine, plus any classes from JAR files present in the System Extensions directory ($JAVA_HOME/jre/lib/ext). Note: some JVMs may implement this as more than one class loader, or it may not be visible (as a class loader) at all.

    4.2 System — 这个classloader通常是由CLASSPATH这个环境变量初始化的,通过这个classloader加载的所有类,都对tomcat自身的类,以及所有web应用的类可见。但是,标准的tomcat启动脚本($CATALINA_HOME/bin/catalina.bat),完全无视默认的CLASSPATH环境变量,而是加载了以下3个.jar

    $CATALINA_HOME/bin/bootstrap.jar — Contains the main() method that is used to initialize the Tomcat server, and the class loader implementation classes it depends on.

    $CATALINA_HOME/bin/tomcat-juli.jar — Logging implementation classes. These include enhancement classes to java.util.logging API, known as Tomcat JULI, and a package-renamed copy of Apache Commons Logging library used internally by Tomcat.

    $CATALINA_HOME/bin/commons-daemon.jar — The classes from Apache Commons Daemon project.(这个类不是直接在$CATALINA_HOME/bin/catalina.bat里加进来的,不过在bootstrap.jar的manifest文件中包含进来了)

    4.3 Common — 这个classloader加载的类,对tomcat的类和web app的类都是可见的。通常来说,应用程序的类不应该放在这里。该加载器的加载路径是在$CATALINA_BASE/conf/catalina.properties文件里,通过common.loader属性来定义的,默认是:

    Text代码

    899e257fdc900833bb965036634b92b3.png

    common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar

    By default, this includes the following:

    annotations-api.jar — JavaEE annotations classes.

    catalina.jar — Implementation of the Catalina servlet container portion of Tomcat.

    catalina-ant.jar — Tomcat Catalina Ant tasks.

    catalina-ha.jar — High availability package.

    catalina-tribes.jar — Group communication package.

    ecj-*.jar — Eclipse JDT Java compiler.

    el-api.jar — EL 2.2 API.

    jasper.jar — Tomcat Jasper JSP Compiler and Runtime.

    jasper-el.jar — Tomcat Jasper EL implementation.

    jsp-api.jar — JSP 2.2 API.

    servlet-api.jar — Servlet 3.0 API.

    tomcat-api.jar — Several interfaces defined by Tomcat.

    tomcat-coyote.jar — Tomcat connectors and utility classes.

    tomcat-dbcp.jar — Database connection pool implementation based on package-renamed copy of Apache Commons Pool and Apache Commons DBCP.

    tomcat-i18n-**.jar — Optional JARs containing resource bundles for other languages. As default bundles are also included in each individual JAR, they can be safely removed if no internationalization of messages is needed.

    tomcat-jdbc.jar — An alternative database connection pool implementation, known as Tomcat JDBC pool. See documentation for more details.

    tomcat-util.jar — Common classes used by various components of Apache Tomcat.

    4.4 WebappX — 该classloader加载所有WEB-INF/classes里的类,以及WEB-INF/lib里的jar

    该classloader就有意违反了上述的委托模型,它首先看WEB-INF/classes和WEB-INF/lib里是否有请求的类,而不是委托parent classloader去加载。但是,JRE里定义的类不能被覆盖(比如java.lang.String),以及Servlet API会明确地被忽略。

    前面说的bootstrap、system、common,都遵循普通的委托模型

    4.5 总的来说,从web app的角度来看,类或者资源加载是按照以下的顺序来查找的:

    Bootstrap classes of your JVM(rt.jar)

    System class loader classes(bootstrap.jar、tomcat-juli.jar、commons-deamon.jar)

    /WEB-INF/classes of your web application

    /WEB-INF/lib/*.jar of your web application

    Common class loader classes (在$CATALINA_HOME/lib里的jar包)

    一、工具准备

    需要SVN、Maven、JDK、Eclipse、M2Eclipse

    二、下载源码及发布包

    源码在:

    http://svn.apache.org/repos/asf/tomcat/tc7.0.x/tags/TOMCAT_7_0_27/

    发布包在:

    http://archive.apache.org/dist/tomcat/tomcat-7/v7.0.27/bin/

    说明:下载发布包这个步骤是可选的,好处是免得从源码再自行构建,节省时间;另外发布包里的配置文件等,接下来可以直接拿来用,很方便

    三、整理目录

    前面下载得到了源码和发布包,现在要把它们放到同一个目录里,再整理一下,方便后面把它转化成eclipse工程,毕竟后续读源码,以及调试,都要在eclipse里完成

    新建一个单独的目录,叫tomcat7.0.27,然后把刚才下载的源码和发布包都放进去。源码目录重命名为code;发布包重命名为launch

    得到的目录结构见下图:

    1ea71081c9b895e619f7acacf8bf12a4.png

    一会就会把这个目录导入eclipse,变成可运行,可调试的eclipse工程

    四、转换成maven工程

    将附件中的pom.xml放入目录,与code、launch目录平行

    得到的目录结构见下图:

    592448e831d89f58038d545e32b33f3f.png

    说明:这也不是必须的,只是为了方便

    五、导入eclipse

    901b70bb2b97188555e93c885fe1d5e7.png

    9cad5760f60f8067701aec2a8b721b95.png

    导入成功以后,eclipse里的工程目录结构如下图:

    aebc4412e813183147fa0eb832f37546.png

    接下来就可以在eclipse里运行和调试tomcat了,也可以随意修改源代码,或者自己添加测试用例

    六、启动tomcat

    tomcat启动入口类是:org.apache.catalina.startup.Bootstrap

    平时我们用发布包启动tomcat一般是用脚本startup.bat或者startup.sh,其实就是在脚本中先处理启动参数和系统变量,然后调用这个入口类的main()方法

    所以在eclipse里启动,我们也是直接执行这个类的main()方法,只是模拟脚本,设置一下启动参数和系统变量

    方法1:

    在VM arguments中,拷贝以下参数

    -Dcatalina.home=launch -Dcatalina.base=launch -Djava.endorsed.dirs=launch/endorsed -Djava.io.tmpdir=launch/temp -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file=launch/conf/logging.properties

    如图:

    6df9915415fcfb3f49d1394a3f989bad.png

    方法2:

    将附件中的启动脚本,拷贝到工程目录下,结构如下图:

    627251ce2064a1f07e2e5df80d1e2b06.png

    然后直接在start-tomcat7.launch上右键点击,run就可以

    启动效果如下图:

    dc311f4bb537f70c00a851c1a39719af.png

    眼熟,和普通的脚本启动,以及启动嵌入式tomcat的信息都是一样的

    最后用浏览器访问:http://localhost:8080/examples/

    七、tomcat7核心架构

    acea31a3409300e41d0ce520607f80d3.png

    包名

    作用

    javax.*

    各种JSR的API,如jsp、servlet、el等

    org.apache.catalina

    tomcat自身架构

    org.apache.coyote

    http、ajp协议实现

    org.apache.el

    EL规范实现

    org.apache.jasper

    JSP规范实现

    org.apche.juli

    日志

    org.apache.naming

    JNDI实现

    org.apache.tomcat

    工具包、XML解析器等

    1、ClassLoader结构

    e536f3c09824924fe20de56c6f25c1b9.png

    tomcat的ClassLoader模型如上图,主要是为了满足servlet规范中类隔离的要求(见JSR154的Section9.4、9.6、9.7)

    1.1 Bootstrap

    这个类加载器和普通的JAVA应用一样,都是由JVM启动的,加载%JAVA_HOME%/jre/lib下的JAR包,如rt.jar等

    通常情况下,Bootstrap和Extension是分开考虑的,但是在tomcat的ClassLoader体系里,没有将二者区分开。当谈到Bootstrap时,就包括了Bootstrap和Extension

    1.2 System

    System类加载器,也叫App类加载器,一般就是启动应用程序的那个加载器,是根据classpath创建的

    但是在tomcat里,完全忽略了默认的classpath,而是根据指定的参数,直接创建System类加载器,默认情况下,会加载%CATALINA_HOME%/bin目录下的bootstrap.jar、tomcat-juli.jar、commons-daemon.jar

    这些jar包充当了启动入口的角色,但是tomcat真正的核心实现类,不是在这个ClassLoader里加载的,所以后面会提到,源码里调用核心实现类(Catalina等)的方法,必须指定ClassLoader,通过反射完成

    1.3 Common

    这个类加载器是tomcat特有的,对于所有web app可见。这个类加载器默认会加载%CATALINA_HOME%/lib下的所有jar包,这些都是tomcat的核心

    1.4 WebappX

    对于部署在容器中的每一个webapp,都有一个独立的ClassLoader,在这里实现了不同应用的类隔离

    这里的ClassLoader与标准的ClassLoader委托模型不同,当需要加载一个类的时候,首先是委托Bootstrap和System;然后尝试自行加载;最后才会委托Common

    加载顺序如下:

    Bootstrap -> System -> WEB-INF/classes -> WEB-INF/lib -> Common

    2、疑问

    Bootstrap.class和Catalina.class是打在不同的JAR包里的。前者在bootstrap.jar里,后者在catalina.jar里

    并且这2个jar包,是由不同的ClassLoader加载的。前者由System ClassLoader加载,后者由Common ClassLoader加载

    这造成代码比较麻烦,Bootstrap.class要引用Catalina.class的时候,不是直接引用,而是通过反射实现

    我还没搞清楚tomcat这样设计的原因,跟类隔离貌似没有直接关系

    3、tomcat启动

    3.1 执行脚本

    tomcat启动是从运行startup.bat脚本开始的,在此脚本中首先会设置一系列环境变量,然后配置参数,最后实际上执行的catalina.bat脚本。所以用catalina.bat start命令,也可以启动tomcat

    3.2 加载Bootstrap类

    在catalina.bat中,会将classpath设置为%CATALINA_HOME%/bin/bootstrap.jar和%CATALINA_HOME%/bin/tomcat-juli.jar,然后根据此classpath创建System类加载器,加载bootstrap.jar中的Bootstrap.class,执行main()方法

    3.3 调用Catalina类

    在Bootstrap.class里,读取配置文件(%CATALINA_HOME%/conf/catalina.properties),然后创建Common ClassLoader,加载%CATALINA_HOME%/lib里的所有jar包

    之后根据实际的命令行参数,调用org.apache.catalina.startup.Catalina中相应的方法,比如start()等

    4、参考文档

    前几天想了一下,最近主要学习linux和httpd,所以tomcat源码阅读先放一放,可能到9月份左右再继续。不过先把已经写好的几篇陆续贴上来

    tomcat用到很多ClassLoader相关的代码,如果缺乏这方面的背景知识,阅读源码会遇到很多障碍,所以本文首先总结一下这方面的内容,和tomcat源码的关系不大

    1 标准的ClassLoader体系

    43fee49b303827945e8dafc97c70d993.png

    1.1 bootstrap

    bootstrap classloader是由JVM启动的,用于加载%JAVA_HOME%/jre/lib/下的JAVA平台自身的类(比如rt.jar中的类等)。这个classloader位于JAVA类加载器链的顶端,是用C/C++开发的,而且JAVA应用中没有任何途径可以获取到这个实例,它是JDK实现的一部分

    1.2 extension

    entension classloader用于加载%JAVA_HOME%/jre/lib/ext/下的类,它的实现类是sun.misc.Launcher$ExtClassLoader,是一个内部类

    基本上,我们开发的JAVA应用都不太需要关注这个类

    1.3 system

    system classloader是jvm启动时,根据classpath参数创建的类加载器(如果没有显式指定classpath,则以当前目录作为classpath)。在普通的JAVA应用中,它是最重要的类加载器,因为我们写的所有类,通常都是由它加载的。这个类加载器的实现类是sun.misc.Launch$AppClassLoader

    用ClassLoader.getSystemClassLoader(),可以得到这个类加载器

    1.4 custom

    一般情况下,对于普通的JAVA应用,ClassLoader体系就到system为止了。平时编程时,甚至都不会感受到classloader的存在

    但是对于其他一些应用,比如web server,插件加载器等,就必须和ClassLoader打交道了。这时候默认的类加载器不能满足需求了(类隔离、运行时加载等需求),需要自定义类加载器,并挂载到ClassLoader链中(默认会挂载到system classloader下面)

    2 双亲委派模型

    从上面的图可以看到,classloader链,是一个自上而下的树形结构。一般来说,java中的类加载,是遵循双亲委派模型的,即:

    当一个classloader要加载一个类时,首先会委托给它的parent classloader来加载,如果parent找不到,才会自己加载。如果最后也找不到,则会抛出熟悉的ClassNotFoundException

    这个模型,是在最基础的抽象类ClassLoader里确定的:

    Java代码

    cf2e1763c8893427a90caf2ff8184290.png

    protected synchronized Class> loadClass(String name, boolean resolve)

    throws ClassNotFoundException

    {

    // 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);

    }

    }

    if (resolve) {

    resolveClass(c);

    }

    return c;

    }

    自定义ClassLoader的时候,一般来说,需要做的并不是覆盖loadClass()方法,这样的话就“破坏”了双亲委派模型;需要做的只是实现findClass()方法即可

    不过,从上面的代码也可以看出,双亲委派模型只是一种“建议”,并没有强制保障的措施。如果自定义的ClassLoader无视此规定,直接自行加载,不将请求委托给parent,当然也是没问题的

    在实际情况中,双亲委派模型被“破坏”也是很常见的。比如在tomcat里,webappx classloader就不会委托给上层的common classloader,而是先委托给system,然后自己加载,最后才委托给common;再比如说在OSGi里,更是有意完全打破了这个规则

    当然,对于普通的JAVA应用开发来说,需要自定义classloader的场景本来就不多,需要去违反双亲委派模型的场景,更是少之又少

    3 自定义ClassLoader

    3.1 自定义ClassLoader的一般做法

    从上面的代码可以看到,自定义ClassLoader很简单,只要继承抽象类ClassLoader,再实现findClass()方法就可以了

    3.2 自定义ClassLoader的场景

    事实上,需要实现新的ClassLoader的场景是很少的

    注意:需要增加一个自定义ClassLoader的场景很多;但是,需要自己实现一个新的ClassLoader子类的场景不多。这是两回事,不可混淆

    比如,即使在tomcat里,也没有自行实现新的ClassLoader子类,只是创建了URLClassLoader的实例,作为custom classloader

    3.3 ClassLoader的子类

    在JDK中已经提供了若干ClassLoader的子类,在需要的时候,可以直接创建实例并使用。其中最常用的是URLClassLoader,用于读取一个URL下的资源,从中加载Class

    Java代码

    cf2e1763c8893427a90caf2ff8184290.png

    @Deprecated

    public class StandardClassLoader

    extends URLClassLoader

    implements StandardClassLoaderMBean {

    public StandardClassLoader(URL repositories[]) {

    super(repositories);

    }

    public StandardClassLoader(URL repositories[], ClassLoader parent) {

    super(repositories, parent);

    }

    }

    可以看到,tomcat就是在URLClassLoader的基础上,包装了StandardClassLoader,实际上并没有任何功能上的区别

    3.4 设置parent

    在抽象类ClassLoader中定义了一个parent字段,保存的是父加载器。但是这个字段是private的,并且没有setter方法

    这就意味着只能在构造方法中,一次性地设置parent classloader。如果没有设置的话,则会默认将system classloader设置为parent,这也是在ClassLoader类中确定的:

    Java代码

    cf2e1763c8893427a90caf2ff8184290.png

    protected ClassLoader(ClassLoader parent) {

    this(checkCreateClassLoader(), parent);

    }

    protected ClassLoader() {

    this(checkCreateClassLoader(), getSystemClassLoader());

    }

    4 ClassLoader隐性传递

    “隐性传递”这个词是我乱造的,在网上和注释中没有找到合适的描述的词

    试想这样一种场景:在应用中需要加载100个类,其中70个在classpath下,默认由system来加载,这部分不需要额外处理;另外30个类,由自定义classloader加载,比如在tomcat里:

    Java代码

    cf2e1763c8893427a90caf2ff8184290.png

    Class> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");

    Object startupInstance = startupClass.newInstance();

    如上,org.apache.catalina.startup.Catalina是由自定义类加载器加载的,需要额外编程来处理(如果是system加载的,直接new就可以了)

    如果30个类,都要通过这种方式来加载,就太麻烦了。不过classloader有一个特性,就是“隐性传递”,即:

    如果一个ClassA是由某个ClassLoader加载的,那么ClassA中依赖的需要加载的类,默认也会由同一个ClassLoader加载

    这个机制是由JVM保证的,对于程序员来说是透明的

    5 current classloader

    5.1 定义

    与前面说的extension、system等不同,在运行时并不存在一个实际的“current classloader”,只是一个抽象的概念。指的是一个类“当前的”类加载器。一个对象实例所属的Class,是由哪一个ClassLoader加载的,这个ClassLoader就是这个对象实例的current classloader

    获得的方法是:

    Java代码

    cf2e1763c8893427a90caf2ff8184290.png

    this.getClass().getClassLoader();

    5.2 实例

    current classloader概念的意义,主要在于它会影响Class.forName()方法的表现,贴一段代码进行说明:

    Java代码

    cf2e1763c8893427a90caf2ff8184290.png

    public class Test {

    public void tryForName() {

    System.out.println("current classloader: "

    + this.getClass().getClassLoader());

    try {

    Class.forName("net.kyfxbl.test.cl.Target");

    System.out.println("load class success");

    } catch (ClassNotFoundException e) {

    e.printStackTrace();

    }

    }

    }

    这个类调用了Class.forName()方法,试图加载net.kyfxbl.test.cl.Target类(Target是一个空类,作为加载目标,不重要)。这个类在运行时能否加载Target成功,取决于它的current classloader,能不能加载到Target

    首先,将Test和Target打成jar包,放到classpath之外,jar包中内容如下:

    7f7f7357ace3c5aec5891162f9e1ea26.png

    然后在工程中删除Target类(classpath中加载不到Target了)

    在Main中用system 加载Test,此时Test的current classloader是system,加载Target类失败

    Java代码

    cf2e1763c8893427a90caf2ff8184290.png

    public static void main(String[] args) {

    Test t = new Test();

    t.tryForName();

    }

    然后,这次用自定义的classloader来加载

    Java代码

    cf2e1763c8893427a90caf2ff8184290.png

    public static void main(String[] args) throws Exception {

    ClassLoader cl = createClassLoader();

    Class> startupClass = cl.loadClass("net.kyfxbl.test.cl.Test");

    Object startupInstance = startupClass.newInstance();

    String methodName = "tryForName";

    Class>[] paramTypes = new Class[0];

    Object[] paramValues = new Object[0];

    Method method = startupInstance.getClass().getMethod(methodName,

    paramTypes);

    method.invoke(startupInstance, paramValues);

    }

    private static ClassLoader createClassLoader() throws MalformedURLException {

    String filePath = "c://hehe.jar";

    File file = new File(filePath);

    URL url = file.toURI().toURL();

    URL[] urls = new URL[] { url };

    ClassLoader myClassLoader = new URLClassLoader(urls);

    return myClassLoader;

    }

    在想象中,这次Test的current classloader应该变成URLClassLoader,并且加载Target成功。但是还是失败了

    这是因为前面说过的“双亲委派模型”,URLClassLoader的parent是system classloader,由于工程里的Test类没有删除,所以classpath里还是能找到Test类,所以Test类的current classloader依然是system classloader,和第一次一样

    ec1eaa68c2ae0f81eb83260551d962b6.png

    接下来把工程里的Test类也删除,这次就成功了

    e34b6151d189eb41e6394d447dabc199.png

    5.3 Class.forName()

    前面说的是单个参数的forName()方法,默认使用current ClassLoader

    除此之外,Class类还定义了3个参数的forName()方法,方法签名如下:

    Java代码

    cf2e1763c8893427a90caf2ff8184290.png

    public static Class> forName(String name, boolean initialize,

    ClassLoader loader)

    throws ClassNotFoundException

    这个方法的最后一个参数,可以传递一个ClassLoader,会用这个ClassLoader进行加载。这个方法很重要

    比如像JNDI,主体类是在JDK包里,由bootstrap加载。而SPI的实现类,则是由厂商提供,一般在classpath里。那么在JNDI的主体类里,要加载SPI的实现类,直接用Class.forName()方法肯定是不行的,这时候就要用到3个参数的Class.forName()方法了

    6 ContextClassLoader

    6.1 获取ClassLoader的API

    前面说过,已经有2种方式可以获取到ClassLoader的引用

    一种是ClassLoader.getSystemClassLoader(),获取的是system classloader

    另一种是getClass().getClassLoader(),获取的是current classloader

    这2种API都只能获取classloader,没有办法用来传递

    6.2 传递ClassLoader

    每一个thread,都有一个contextClassLoader,并且有getter和setter方法,用来在线程之间传递ClassLoader

    有2条默认的规则:

    首先,contextClassLoader默认是继承的,在父线程中创建子线程,那么子线程会继承父线程的contextClassLoader

    其次,主线程,也就是执行main()方法的那个线程,默认的contextClassLoader是system classloader

    6.3 例子

    对上面例子中的Test和Main稍微改一下(Test和Target依然打到jar包里,然后从工程中删除,避免被system classloader加载到)

    Java代码

    cf2e1763c8893427a90caf2ff8184290.png

    public class Test {

    public void tryForName() {

    System.out.println("current classloader: "

    + getClass().getClassLoader());

    System.out.println("thread context classloader: "

    + Thread.currentThread().getContextClassLoader());

    try {

    Class.forName("net.kyfxbl.test.cl.Target");

    System.out.println("load class success");

    } catch (Exception exc) {

    exc.printStackTrace();

    }

    }

    }

    Java代码

    cf2e1763c8893427a90caf2ff8184290.png

    public static void main(String[] args) throws Exception {

    ClassLoader cl = createClassLoader();

    Class> startupClass = cl.loadClass("net.kyfxbl.test.cl.Test");

    final Object startupInstance = startupClass.newInstance();

    new Thread(new Runnable() {

    @Override

    public void run() {

    String methodName = "tryForName";

    Class>[] paramTypes = new Class[0];

    Object[] paramValues = new Object[0];

    try {

    Method method = startupInstance.getClass().getMethod(

    methodName, paramTypes);

    method.invoke(startupInstance, paramValues);

    } catch (Exception exc) {

    exc.printStackTrace();

    }

    }

    }).start();

    }

    这次的tryForName()方法在一个子线程中被调用,并依次打印出current classloader和contextClassLoader,如图:

    18216594594deb88173003b867ad77ca.png

    可以看到,子线程继承了父线程的contextClassLoader

    同时可以注意到,contextClassLoader对Class.forName()方法没有影响,contextClassLoader只是起到在线程之间传递ClassLoader的作用

    6.4 题外话

    从这个例子还可以看出,一个方法在运行时的表现,在编译期是无法确定的

    在运行时的表现,有时候取决于方法所在的类是被哪个ClassLoader加载;有时候取决于是运行在单线程环境下,还是多线程环境下

    这在编译期是不可知的,所以在编程的时候,要考虑运行时的情况。比如所谓“线程安全”的类,并不是说它“一定”会运行在多线程环境下,而是说它“可以”运行在多线程环境下

    7 总结

    本文大致总结了ClassLoader的背景知识。掌握了背景,再阅读tomcat的源码,基本就不会遇到ClassLoader方面的困难

    本文介绍了ClassLoader的标准体系、双亲委派模型、自定义ClassLoader的方法、以及current classloader和contextClassLoader的概念

    其中最重要的是current classloader和contextClassLoader

    用于获取ClassLoader的API主要有3种:

    ClassLoader.getSystemClassLoader();

    Class.getClassLoader();

    Thread.getContextClassLoader();

    第一个是静态方法,返回的永远是system classloader,也就是说,没什么用

    后面2个都是实例方法,一个是返回实例所属的类的ClassLoader;另一个返回当前线程的contextClassLoader,具体的结果都要在运行时才能确定

    其中,contextClassLoader可以起到传递ClassLoader的作用,所以特别重要

    展开全文
  • 第一步:下载Tomcat源码 下载地址:http://archive.apache.org/dist/tomcat/tomcat-9/v9.0.0.M22/src/ 第二部:解压zip包并用IDEA打开 新增:pom.xml以及calina_home pom.xml中的内容: <?xml ...

    第一步:下载Tomcat 源码

    下载地址:http://archive.apache.org/dist/tomcat/tomcat-8/v8.5.63/src/

    第二部:解压zip 包 并用IDEA 打开

    新增:pom.xml 以及 catalina-home 并将conf 文件夹和 webapps 文件夹移动到catalina-home目录下

     

    pom.xml 中的内容:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>org.apache.tomcat</groupId>
    
        <artifactId>Tomcat8.0</artifactId>
    
        <name>Tomcat8.0</name>
    
        <version>8.0</version>
    
        <build>
    
            <finalName>Tomcat8.0</finalName>
    
            <sourceDirectory>java</sourceDirectory>
    
            <resources>
    
                <resource>
    
                    <directory>java</directory>
    
                </resource>
    
            </resources>
    
            <testResources>
    
                <testResource>
    
                    <directory>test</directory>
    
                </testResource>
    
            </testResources>
    
            <plugins>
    
                <plugin>
    
                    <groupId>org.apache.maven.plugins</groupId>
    
                    <artifactId>maven-compiler-plugin</artifactId>
    
                    <version>2.3</version>
    
                    <configuration>
    
                        <encoding>UTF-8</encoding>
    
                        <source>1.8</source>
    
                        <target>1.8</target>
    
                    </configuration>
    
                </plugin>
    
            </plugins>
    
        </build>
    
        <dependencies>
    
            <dependency>
    
                <groupId>junit</groupId>
    
                <artifactId>junit</artifactId>
    
                <version>4.12</version>
    
                <scope>test</scope>
    
            </dependency>
    
            <dependency>
    
                <groupId>org.easymock</groupId>
    
                <artifactId>easymock</artifactId>
    
                <version>3.4</version>
    
            </dependency>
    
            <dependency>
    
                <groupId>ant</groupId>
    
                <artifactId>ant</artifactId>
    
                <version>1.7.0</version>
    
            </dependency>
    
            <dependency>
    
                <groupId>wsdl4j</groupId>
    
                <artifactId>wsdl4j</artifactId>
    
                <version>1.6.2</version>
    
            </dependency>
    
            <dependency>
    
                <groupId>javax.xml</groupId>
    
                <artifactId>jaxrpc</artifactId>
    
                <version>1.1</version>
    
            </dependency>
    
            <dependency>
    
                <groupId>org.eclipse.jdt.core.compiler</groupId>
    
                <artifactId>ecj</artifactId>
    
                <version>4.5.1</version>
    
            </dependency>
    
        </dependencies>
    
    </project>
    
    

    第三步:配置好该项目maven,jdk 配置(自行百度)

    第四步:配置该项目

     

    其中:VM options: 配置内容是:

    -Dcatalina.home=catalina-home -Dcatalina.base=catalina-home -Djava.endorsed.dirs=catalina-home/endorsed -Djava.io.tmpdir=catalina-home/temp -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file=catalina-home/conf/logging.properties

    第四步:鼠标右键pom.xml 将该项目添加为maven 项目,就会在右边出现该项目

    第五步:clean  package,install

     

     

    注释:将class

    JmxRemoteLifecycleListener 全文注释掉,这个类已经过时编译会报错

     

    最后编译成功:

    启动后乱码处理:找StringManager 类 找到下面对应的方法,加上那段代码

    展开全文
  • Apache Tomcat源码

    2021-06-05 19:32:13
    Apache Tomcat源码Apache Tomcat源码1 Apache Tomcat源码环境构建1.1 Apache Tomcat源码下载1.2 Tomcat源码环境配置1.2.1 增加POM依赖管理文件1.2.3 IDEA环境导入与启动2 Tomcat架构与源码剖析2.1 Apache Tomcat总体...

    Apache Tomcat源码

    1 Apache Tomcat源码环境构建

    1.1 Apache Tomcat源码下载

    https://tomcat.apache.org/download-80.cgi
    下载对应的zip包
    在这里插入图片描述

    1.2 Tomcat源码环境配置

    1.2.1 增加POM依赖管理文件

    解压 apache-tomcat-8.5.63-src压缩包,进src陆慕,创建一个pom.xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
    http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>apache-tomcat-8.5.63-src</artifactId>
        <name>Tomcat8.5</name>
        <version>8.5</version>
        <build>
            <!--指定源⽬录-->
            <finalName>Tomcat8.5</finalName>
            <sourceDirectory>java</sourceDirectory>
            <resources>
                <resource>
                    <directory>java</directory>
                </resource>
            </resources>
            <plugins>
                <!--引⼊编译插件-->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <encoding>UTF-8</encoding>
                        <source>11</source>
                        <target>11</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>
        <!--tomcat 依赖的基础包-->
        <dependencies>
            <dependency>
                <groupId>org.easymock</groupId>
                <artifactId>easymock</artifactId>
                <version>3.4</version>
            </dependency>
            <dependency>
                <groupId>ant</groupId>
                <artifactId>ant</artifactId>
    
                <version>1.7.0</version>
            </dependency>
            <dependency>
                <groupId>wsdl4j</groupId>
                <artifactId>wsdl4j</artifactId>
                <version>1.6.2</version>
            </dependency>
            <dependency>
                <groupId>javax.xml</groupId>
                <artifactId>jaxrpc</artifactId>
                <version>1.1</version>
            </dependency>
            <dependency>
                <groupId>org.eclipse.jdt.core.compiler</groupId>
                <artifactId>ecj</artifactId>
                <version>4.5.1</version>
            </dependency>
            <dependency>
                <groupId>javax.xml.soap</groupId>
                <artifactId>javax.xml.soap-api</artifactId>
                <version>1.4.0</version>
            </dependency>
        </dependencies>
    </project>
    

    1.2.3 IDEA环境导入与启动

    idea导入maven项目,注意环境:
    idea: 2020.3
    jdk: 11

    执行 Bootstrap.java 的main方法即可,非常简单

    1)常见错误一

    Error:(505, 53) java: 程序包 sun.rmi.registry 不可见 (程序包 sun.rmi.registry 已在模块 java.rmi 中声明, 但该模块未将它导出到未命名模块)
    在这里插入图片描述
    原因:sun的包对ide编译环境不可见造成的,鼠标放在代码中报红的地方,根据idea的提示操作即可。
    在这里插入图片描述
    注意!不要用maven去编译它,这个参数你加入的是idea的环境,所以,用这里:

    2)常见错误二

    运 行Bootstrap 类的 main 函数,此时就启动了tomcat,启动时候会去加载所配置的 conf 录下 的server.xml等配置 件,所以访问8080端 即可,但此时我们会遇到如下的 个错误
    在这里插入图片描述
    原因是Jsp引擎Jasper没有被初始化,从而无法编译JSP,我们需要在tomcat的源码ContextConfig类中 的configureStart 中增加代码将 Jsp 引擎初始化,如下
    org.apache.catalina.startup.ContextConfig#configureStart

       webConfig();
            //初始化JSP解析引擎
            context.addServletContainerInitializer(new JasperInitializer(),null);
    
            if (!context.getIgnoreAnnotations()) {
                applicationAnnotationsConfig();
            }
            if (ok) {
                validateSecurityRoles();
            }
    

    启动Boostrap文件

    http://localhost:8080/
    

    在这里插入图片描述
    可以看到,tomcat成功启动。

    2 Tomcat架构与源码剖析

    2.1 Apache Tomcat总体架构

    在这里插入图片描述
    从Tomcat安装目录下的/conf/server.xml 文件里可以看到最顶层的是server。
    对照上面的关系图,一个Tomcat实例对应一个server,一个 Server 中有一个或者多个 Service,
    一个 Service 中有多个连接器和一个容器,Service组件本身没做其他事
    只是把连接器和容器组装起来。连接器与容器之间通过标准的 ServletRequest 和 ServletResponse 通 信

    Server:Server容器就代表一个Tomcat实例(Catalina实例),其下可以有一个或者多个Service容器;
    Service:Service是提供具体对外服务的(默认只有一个),一个Service容器中又可以有多个Connector组件(监听不同端口请求,解析请求)和一个Servlet容器(做具体的业务逻辑处理);
    Engine和Host:Engine组件(引擎)是Servlet容器Catalina的核心,它支持在其下定义多个虚拟主机(Host),虚拟主机允许Tomcat引擎在将配置在一台机器上的多个域名,比如www.baidu.com、www.bat.com分割开来互不干扰;
    Context:每个虚拟主机又可以支持多个web应用部署在它下边,这就是我们所熟知的上下文对象
    Context,上下文是使用由Servlet规范中指定的Web应用程序格式表示,不论是压缩过的war包形式的文件还是未压缩的目录形式;
    Wrapper:在上下文中又可以部署多个servlet,并且每个servlet都会被一个包装组件(Wrapper)所包含(一个wrapper对应一个servlet)

    去掉注释的server.xml
    在这里插入图片描述
    具体配置如下

    <?xml version="1.0" encoding="UTF-8"?>
    <!--
      Licensed to the Apache Software Foundation (ASF) under one or more
      contributor license agreements.  See the NOTICE file distributed with
      this work for additional information regarding copyright ownership.
      The ASF licenses this file to You under the Apache License, Version 2.0
      (the "License"); you may not use this file except in compliance with
      the License.  You may obtain a copy of the License at
    
          http://www.apache.org/licenses/LICENSE-2.0
    
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      See the License for the specific language governing permissions and
      limitations under the License.
    -->
    <!-- Note:  A "Server" is not itself a "Container", so you may not
         define subcomponents such as "Valves" at this level.
         Documentation at /docs/config/server.html
     -->
    <Server port="8005" shutdown="SHUTDOWN">
      <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
      <!-- Security listener. Documentation at /docs/config/listeners.html
      <Listener className="org.apache.catalina.security.SecurityListener" />
      -->
      <!--APR library loader. Documentation at /docs/apr.html -->
      <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
      <!-- Prevent memory leaks due to use of particular java/javax APIs-->
      <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
      <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
      <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
    
      <!-- Global JNDI resources
           Documentation at /docs/jndi-resources-howto.html
      -->
      <GlobalNamingResources>
        <!-- Editable user database that can also be used by
             UserDatabaseRealm to authenticate users
        -->
        <Resource name="UserDatabase" auth="Container"
                  type="org.apache.catalina.UserDatabase"
                  description="User database that can be updated and saved"
                  factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
                  pathname="conf/tomcat-users.xml" />
      </GlobalNamingResources>
    
      <!-- A "Service" is a collection of one or more "Connectors" that share
           a single "Container" Note:  A "Service" is not itself a "Container",
           so you may not define subcomponents such as "Valves" at this level.
           Documentation at /docs/config/service.html
       -->
      <Service name="Catalina">
    
        <!--The connectors can use a shared executor, you can define one or more named thread pools-->
        <!--
        <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
            maxThreads="150" minSpareThreads="4"/>
        -->
    
    
        <!-- A "Connector" represents an endpoint by which requests are received
             and responses are returned. Documentation at :
             Java HTTP Connector: /docs/config/http.html
             Java AJP  Connector: /docs/config/ajp.html
             APR (HTTP/AJP) Connector: /docs/apr.html
             Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
        -->
        <Connector port="8080" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   redirectPort="8443" />
        <!-- A "Connector" using the shared thread pool-->
        <!--
        <Connector executor="tomcatThreadPool"
                   port="8080" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   redirectPort="8443" />
        -->
        <!-- Define an SSL/TLS HTTP/1.1 Connector on port 8443
             This connector uses the NIO implementation. The default
             SSLImplementation will depend on the presence of the APR/native
             library and the useOpenSSL attribute of the
             AprLifecycleListener.
             Either JSSE or OpenSSL style configuration may be used regardless of
             the SSLImplementation selected. JSSE style configuration is used below.
        -->
        <!--
        <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
                   maxThreads="150" SSLEnabled="true">
            <SSLHostConfig>
                <Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
                             type="RSA" />
            </SSLHostConfig>
        </Connector>
        -->
        <!-- Define an SSL/TLS HTTP/1.1 Connector on port 8443 with HTTP/2
             This connector uses the APR/native implementation which always uses
             OpenSSL for TLS.
             Either JSSE or OpenSSL style configuration may be used. OpenSSL style
             configuration is used below.
        -->
        <!--
        <Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
                   maxThreads="150" SSLEnabled="true" >
            <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
            <SSLHostConfig>
                <Certificate certificateKeyFile="conf/localhost-rsa-key.pem"
                             certificateFile="conf/localhost-rsa-cert.pem"
                             certificateChainFile="conf/localhost-rsa-chain.pem"
                             type="RSA" />
            </SSLHostConfig>
        </Connector>
        -->
    
        <!-- Define an AJP 1.3 Connector on port 8009 -->
        <!--
        <Connector protocol="AJP/1.3"
                   address="::1"
                   port="8009"
                   redirectPort="8443" />
        -->
    
        <!-- An Engine represents the entry point (within Catalina) that processes
             every request.  The Engine implementation for Tomcat stand alone
             analyzes the HTTP headers included with the request, and passes them
             on to the appropriate Host (virtual host).
             Documentation at /docs/config/engine.html -->
    
        <!-- You should set jvmRoute to support load-balancing via AJP ie :
        <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
        -->
        <Engine name="Catalina" defaultHost="localhost">
    
          <!--For clustering, please take a look at documentation at:
              /docs/cluster-howto.html  (simple how to)
              /docs/config/cluster.html (reference documentation) -->
          <!--
          <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
          -->
    
          <!-- Use the LockOutRealm to prevent attempts to guess user passwords
               via a brute-force attack -->
          <Realm className="org.apache.catalina.realm.LockOutRealm">
            <!-- This Realm uses the UserDatabase configured in the global JNDI
                 resources under the key "UserDatabase".  Any edits
                 that are performed against this UserDatabase are immediately
                 available for use by the Realm.  -->
            <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                   resourceName="UserDatabase"/>
          </Realm>
    
          <Host name="localhost"  appBase="webapps"
                unpackWARs="true" autoDeploy="true">
    
            <!-- SingleSignOn valve, share authentication between web applications
                 Documentation at: /docs/config/valve.html -->
            <!--
            <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
            -->
    
            <!-- Access log processes all example.
                 Documentation at: /docs/config/valve.html
                 Note: The pattern used is equivalent to using pattern="common" -->
            <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
                   prefix="localhost_access_log" suffix=".txt"
                   pattern="%h %l %u %t &quot;%r&quot; %s %b" />
    
          </Host>
          <Host name="localhost2"  appBase="webapps2"
                unpackWARs="true" autoDeploy="true">
    
            <!-- SingleSignOn valve, share authentication between web applications
                 Documentation at: /docs/config/valve.html -->
            <!--
            <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
            -->
    
            <!-- Access log processes all example.
                 Documentation at: /docs/config/valve.html
                 Note: The pattern used is equivalent to using pattern="common" -->
            <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
                   prefix="localhost_access_log" suffix=".txt"
                   pattern="%h %l %u %t &quot;%r&quot; %s %b" />
    
          </Host>
        </Engine>
      </Service>
    </Server>
    
    

    2.2 Apache Tomcat连接器

    负责对外交流的连接器(Connector)
    连接器主要功能:
    1、网络通信
    2、用层协议解析读取请求数据
    3、将Tomcat 的Request/Response转成标准的Servlet Request/Response
    因此Tomcat设计者又设计了三个组件来完成这个三个功能,分别是EndPoint、Processor和Adaptor,其中EndPoint和Processor又一起抽象成ProtocalHandler组件,画图理解下
    这里大家先有个印象,下面源码会看到互相之间的调用
    在这里插入图片描述下面的源码我们会详细看到处理的转交过程:
    Connector 给 handler, handler最终调用 endpoint
    Processor 负责提供 Tomcat Request 对象给 Adapter
    Adapter 负责提供 ServletRequest 对象给容器

    2.3 Apache Tomcat源码剖析

    重点分析两个阶段:启动,请求

    2.3.1 start.sh如何启动

    用过Tomcat的我们都知道,可以通过Tomcat的/bin目录下的脚本startup.sh来启动Tomcat,那么这个脚本肯定就是Tomcat的启动入口了,执行过这个脚本之后发生了什么呢?

    在这里插入图片描述
    1、Tomcat本质上也是一个Java程序,因此startup.sh脚本会启动一个JVM来运行Tomcat的启动类Bootstrap
    2、Bootstrap的主要任务是初始化Tomcat的类加载器,并且创建Catalina。
    3、Catalina是一个启动类,它通过解析server.xml,创建相应的组件,并调用 Server的start方法
    4、Server组件的职责就是管理Service组件,它会负责调用Service的start方法
    5、Service组件的职责就是管理连接器和顶层容器Engine,它会调用连接器和 Engine的start方法
    6、Engine组建负责启动管理子容器,通过调用Host的start方法,将Tomcat各层容器启动起来(这里是分层级的,上层容器管理下层容器)

    2.3.2 生命周期统一管理组件

    LifeCycle接口
    Tomcat要启动,肯定要把架构中提到的组件进行实例化(实例化创建–>销毁等:生命周期)。
    Tomcat中那么多组件,为了统一规范他们的生命周期,Tomcat抽象出了LifeCycle生命周期接口
    大家先知道这个内部的类关系,这是一个接口,server.xml 里的哥哥节点都是它的实现类

    LifeCycle生命周期接口方法:
    在这里插入图片描述
    源码如下

    public interface Lifecycle { 
    // 添加监听器 
    public void addLifecycleListener(LifecycleListener listener); 
    // 获取所有监听器 
    public LifecycleListener[] findLifecycleListeners(); 
    // 移除某个监听器 
    public void removeLifecycleListener(LifecycleListener listener); 
    // 初始化方法 
    public void init() throws LifecycleException; 
    ......................领略 
    }
    
    

    这里我们把LifeCycle接口定义分为两部分
    一部分是组件的生命周期方法,比如init()、start()、stop()、destroy()。
    另一部分是扩展接口就是状态和监听器。

    因为所有的组件都实现了LifeCycle接口,
    在父组件的init()方法里创建子组件并调用子组件的init()方法,
    在父组件的start()方法里调用子组件的start()方法,
    那么调用者就可以无差别的只调用最顶层组件,也就是Server组件的init()和start()方法,整个Tomcat就被启动起来了

    2.3.3 Tomcat启动入口在哪里

    (1)启动流程图
    startup.sh --> catalina.sh start --> java xxxx.jar org.apache.catalina.startup.Bootstrap(main)start(参数)
    在这里插入图片描述
    Bootstrap.init
    Catalina.load
    Catalina.start

     //伪代码:调用关系,我们重点看下面标注的 1 2 3 
    //startup.bat 或 sh 
    Bootstrap{ 
      main(){ 
        init(); // 1 
        load(){ // 2 
          Catalina.load(){ 
            createServer(); 
            Server.init(){ 
              Service.init(){ 
                Engine.init(){ 
                  Host.init(){ 
                    Context.init(); 
                   } 
                  }
                 Executor.init(); 
                 Connector.init(){ //8080 
                   ProtocolHaldler.init(){ 
                     EndPoint.init(); 
                   } 
                 } 
                } 
               } 
              } 
             }
            start(){ // 3 
            //与load方法一致 
            } 
           } 
    }
    
    

    (2)系统配置与入口
    Bootstrap类的main方法

    // 知识点【需要debug学习的几个点】 
    // BootStrap static 块 : 确定Tomcat运行环境的根目录 
    // main里的init : 入口 
    // CatalinaProperties: 配置信息加载与获取工具类 
    // static { loadProperties() }: 加载
    
    

    2.3.4 Bootstrap的init方法剖析

    目标
    //1、初始化类加载器 //2、加载catalina类,并且实例化 //3、反射调用Catalina的
    setParentClassLoader方法 //4、实例 赋值
    在这里插入图片描述

     //1、初始化类加载器
        //2、加载catalina类,并且实例化
        //3、反射调用Catalina的setParentClassLoader方法
        //4、实例 赋值
        public void init() throws Exception {
            // 1. 初始化Tomcat类加载器(3个类加载器)
            initClassLoaders();  // ===> classloader , 3个
            // 用上面生成的catalinaLoader作为启动线程的上下文类加载器
            Thread.currentThread().setContextClassLoader(catalinaLoader);
    
            SecurityClassLoad.securityClassLoad(catalinaLoader);
    
            // Load our startup class and call its process() method
            if (log.isDebugEnabled())
                log.debug("Loading startup class");
            // 2. 用loader来实例化Catalina实例【重要节点】
            Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
            Object startupInstance = startupClass.getConstructor().newInstance();
    
            // Set the shared extensions class loader
            if (log.isDebugEnabled())
                log.debug("Setting startup class properties");
            String methodName = "setParentClassLoader";
            Class<?>[] paramTypes = new Class[1];
            paramTypes[0] = Class.forName("java.lang.ClassLoader");
            Object[] paramValues = new Object[1];
            paramValues[0] = sharedLoader;
            // 3. 反射调用Catalina的setParentClassLoader方法,
            // 将sharedLoader设置为Catalina的parentClassLoader成员变量
            // 思考一下,为什么???
            //  ----  为了让 Catalina类去加载 shared下的一堆对象,注意看tomcat的classloader继承关系
            //  后面的webappLoader,jsploader 都是shared的子加载器。
            Method method =
                    startupInstance.getClass().getMethod(methodName, paramTypes);
            method.invoke(startupInstance, paramValues);
            //4、将catalina实例赋值
            catalinaDaemon = startupInstance;
    
            // 结论: bootstrap只是个启动点和流程控制,真正干活的,交给了 catalina类,我们下面重点看它的动作
        }
    
    

    2.3.4 Catalina的load方法剖析

    org.apache.catalina.startup.Bootstrap#main中的load方法
    调用的是catalina中的方法
    1)load初始化流程
    load(包括下面的start)的调用流程核心技术在于,这些类都实现了 2.3.2 里的 生命周期接口。每个节点自己完成的任务后,会接着调用子节点(如果有的话)的同样的方法,引起链式反应。
    反映到流程图如下,下面的debug,包括start我们以图跟代码结合debug:
    在这里插入图片描述
    2)load初始化源码
    Catalina的load方法:

      // 1. 解析server.xml,实例化各Tomcat组件
        // 2. 为Server组件实例设置Catalina相关成员value
        // 3. 调用Server组件的init方法,初始化Tomcat各组件
        public void load() {
    
            if (loaded) {
                return;
            }
            loaded = true;
    
            long t1 = System.nanoTime();
    
            initDirs();  // 空的,不管他
    
            // Before digester - it may be needed
            initNaming();  // 加一堆额外属性值,不管他
    
            // 创建Digester,解析server.xml文件, 这个对象定义了server.xml的结构
            Digester digester = createStartDigester();  // ===>  server.xml的结构定义
    
            InputSource inputSource = null;
            InputStream inputStream = null;
            File file = null;
            try {
                try {
                    file = configFile();// ===>  其实就是server.xml
                    inputStream = new FileInputStream(file);
                    inputSource = new InputSource(file.toURI().toURL().toString());
                } catch (Exception e) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("catalina.configFail", file), e);
                    }
                }
                if (inputStream == null) {
                    try {
                        inputStream = getClass().getClassLoader()
                            .getResourceAsStream(getConfigFile());
                        inputSource = new InputSource
                            (getClass().getClassLoader()
                             .getResource(getConfigFile()).toString());
                    } catch (Exception e) {
                        if (log.isDebugEnabled()) {
                            log.debug(sm.getString("catalina.configFail",
                                    getConfigFile()), e);
                        }
                    }
                }
                // 如果没有,找  server-embed.xml
                // This should be included in catalina.jar  (这货有可能藏在这个jar里)
                // Alternative: don't bother with xml, just create it manually.
                if (inputStream == null) {
                    try {
                        inputStream = getClass().getClassLoader()
                                .getResourceAsStream("server-embed.xml");
                        inputSource = new InputSource
                        (getClass().getClassLoader()
                                .getResource("server-embed.xml").toString());
                    } catch (Exception e) {
                        if (log.isDebugEnabled()) {
                            log.debug(sm.getString("catalina.configFail",
                                    "server-embed.xml"), e);
                        }
                    }
                }
    
    
                if (inputStream == null || inputSource == null) {
                    if  (file == null) {
                        log.warn(sm.getString("catalina.configFail",
                                getConfigFile() + "] or [server-embed.xml]"));
                    } else {
                        log.warn(sm.getString("catalina.configFail",
                                file.getAbsolutePath()));
                        if (file.exists() && !file.canRead()) {
                            log.warn("Permissions incorrect, read permission is not allowed on the file.");
                        }
                    }
                    return;
                }
    
                try {
                    inputSource.setByteStream(inputStream);
                    digester.push(this);  // 把当前catalina交给 digester
                    //1. digester解析server.xml,实例化各Tomcat组件,
                    // 实例化好后,设置进上面的this,也就是catalina对象中
                    digester.parse(inputSource);
                    // parse后,debug变量,仔细看一下 catalina里的值信息,server - service  - engine  - child(host)
                } catch (SAXParseException spe) {
                    log.warn("Catalina.start using " + getConfigFile() + ": " +
                            spe.getMessage());
                    return;
                } catch (Exception e) {
                    log.warn("Catalina.start using " + getConfigFile() + ": " , e);
                    return;
                }
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        // Ignore
                    }
                }
            }
         // 3. 为Server组件实例设置Catalina相关成员value
            getServer().setCatalina(this);
            getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
            getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
    
            // Stream redirection
            initStreams();
    
            // Start the new server。
            /*
            【重要节点】:
            回顾上面,解析完的catalina结构,内部完整的包含了子节点的信息,像一棵树
            这些所有的节点,都实现了 LifeSycle接口,有统一的方法。
            那么,唤起最外层的 server.init() 后,在自己的init完成后,调子节点的 init
            引起连锁反应,最终整个体系的 init 完成!
            * */
            try {
                getServer().init();// ===>  3. 调用Server组件的init方法,初始化Tomcat各组件 调用LifecycleBase.init
            } catch (LifecycleException e) {
                if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                    throw new java.lang.Error(e);
                } else {
                    log.error("Catalina.start", e);
                }
            }
    
            long t2 = System.nanoTime();
            if(log.isInfoEnabled()) {
                log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
            }
        }
    
    

    进入到catalina的load方法,即可开启链式反应……

    // 1. 解析server.xml,实例化各Tomcat组件 
    // 2. 为Server组件实例设置Catalina相关成员value 
    // 3. 调用Server组件的init方法,初始化Tomcat各组件, 开启链式反应的点!
    

    3)关键点
    load这里,一堆的节点,其实其他并不重要,我们重点看Connector的init
    这涉及到tomcat的一个核心问题: 它到底是如何准备好接受请求的!

     try {
    //            初始化protocolHandler组件
                protocolHandler.init();  // ===> 进!
            } catch (Exception e) {
                throw new LifecycleException(
                        sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
            }
    

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    2.3.5 Catalina的start方法剖析

    1)start初始化流程
    流程图与load过程很相似
    在这里插入图片描述
    2)start启动源码
    Catalina的start方法

     public void start() throws Exception {
            if (catalinaDaemon == null) {
                init();
            }
            //调用catalina的start方法,启动Tomcat的所有组件
            Method method = catalinaDaemon.getClass().getMethod("start", (Class[]) null);
            method.invoke(catalinaDaemon, (Object[]) null);  // ===>  往catalina追 start方法!
        }
    
    
    /**
         * Start a new server instance.
         * 启动一个server实例
         */
        public void start() {
    
            if (getServer() == null) {
                load();  //  检查非空,null的话先load
            }
    
            if (getServer() == null) {
                log.fatal("Cannot start server. Server instance is not configured.");
                return;
            }
    
            long t1 = System.nanoTime();
    
            // Start the new server
            try {
                getServer().start();//===> 启动server!!!!!
            } catch (LifecycleException e) {
                log.fatal(sm.getString("catalina.serverStartFail"), e);
                try {
                    getServer().destroy();
                } catch (LifecycleException e1) {
                    log.debug("destroy() failed for failed Server ", e1);
                }
                return;
            }
    
            long t2 = System.nanoTime();
            if(log.isInfoEnabled()) {
                log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
            }
    
            // Register shutdown hook
            if (useShutdownHook) {
                if (shutdownHook == null) {
                    shutdownHook = new CatalinaShutdownHook();
                }
                Runtime.getRuntime().addShutdownHook(shutdownHook);
    
                // If JULI is being used, disable JULI's shutdown hook since
                // shutdown hooks run in parallel and log messages may be lost
                // if JULI's hook completes before the CatalinaShutdownHook()
                LogManager logManager = LogManager.getLogManager();
                if (logManager instanceof ClassLoaderLogManager) {
                    ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                            false);
                }
            }
    
            if (await) {
                await();   // ===>  这里留一个尾巴,【关键点!】  2.3.6的秘密就藏在这里
                stop();   // 如果 8005 接受到shutdown,上面的await会跳出,到这里 , 一堆停止和释放资源操作,忽略……
            }
        }
    
    
    

    3)关键点
    Connector.java 的 start
    我们直接把断点打在 Connector.java 的 startInterval()

       /**
         * 启动一个servier实例(模板方法)
         */
        @Override
        public final synchronized void start() throws LifecycleException {
            // 巴拉巴拉一堆判断和校验操作,不用管它,往下看!
            if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
                    LifecycleState.STARTED.equals(state)) {
    
                if (log.isDebugEnabled()) {
                    Exception e = new LifecycleException();
                    log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
                } else if (log.isInfoEnabled()) {
                    log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
                }
    
                return;
            }
    
            if (state.equals(LifecycleState.NEW)) {
                init();
            } else if (state.equals(LifecycleState.FAILED)) {
                stop();
            } else if (!state.equals(LifecycleState.INITIALIZED) &&
                    !state.equals(LifecycleState.STOPPED)) {
                invalidTransition(Lifecycle.BEFORE_START_EVENT);
            }
    
            try {
                setStateInternal(LifecycleState.STARTING_PREP, null, false);
                startInternal();//===>  这里!与init同一种模式,触发子类的方法,
                // debug into,F7,第一次就是 server ,开始启动!!!!!
    
                if (state.equals(LifecycleState.FAILED)) {
                    // This is a 'controlled' failure. The component put itself into the
                    // FAILED state so call stop() to complete the clean-up.
                    stop();
                } else if (!state.equals(LifecycleState.STARTING)) {
                    // Shouldn't be necessary but acts as a check that sub-classes are
                    // doing what they are supposed to.
                    invalidTransition(Lifecycle.AFTER_START_EVENT);
                } else {
                    setStateInternal(LifecycleState.STARTED, null, false);
                }
            } catch (Throwable t) {
                // This is an 'uncontrolled' failure so put the component into the
                // FAILED state and throw an exception.
                handleSubClassException(t, "lifecycleBase.startFail", toString());
            }
        }
    
    

    在这里插入图片描述

    2.3.6 请求的处理

    启动完就该接受请求了!
    那么请求是如何被tomcat接受并响应的???
    在调试请求前,必须有个请求的案例,我们先来实现它

    1)案例
    在这里插入图片描述
    源码:
    DemoServlet.java

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    public class DemoServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("-----do get----");
        }
    }
    

    web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!--
     Licensed to the Apache Software Foundation (ASF) under one or more
      contributor license agreements.  See the NOTICE file distributed with
      this work for additional information regarding copyright ownership.
      The ASF licenses this file to You under the Apache License, Version 2.0
      (the "License"); you may not use this file except in compliance with
      the License.  You may obtain a copy of the License at
    
          http://www.apache.org/licenses/LICENSE-2.0
    
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      See the License for the specific language governing permissions and
      limitations under the License.
    -->
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                          http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
             version="3.1"
             metadata-complete="true">
        <servlet>
            <servlet-name>demoServlet</servlet-name>
            <servlet-class>com.itheima.test.DemoServlet</servlet-class>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>demoServlet</servlet-name>
            <url-pattern>/test.do</url-pattern>
        </servlet-mapping>
    </web-app>
    
    

    debug重启tomcat,访问 http://localhost:8080/demo/test.do
    确认控制台打印信息,打断点可以正常进来:

    在这里插入图片描述
    基于请求的环境准备工作完成!

    2)url的解析
    回顾开篇,server.xml 、 url与对应的容器:
    http://localhost:8080/demo/test.do
    localhost: Host
    8080: Connector
    demo: Context
    test.do: Url

    3)类关系
    tomcat靠Mapper来完成对url各个部分的映射
    idea追踪MapperElement的继承实现
    从MappedHost类打开入口,看拥有的属性和关系
    在这里插入图片描述
    4)接受请求的流程
    在这里插入图片描述
    5)代码追踪
    温馨提示:征程开始,下面将是漫长的debug之路。别跟丢了!

    代码入口:

     @Override
        public void startInternal() throws Exception {
            // 一堆判断,不去管它,直接整到最下面!
            if (!running) {
                running = true;
                paused = false;
    
                processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                        socketProperties.getProcessorCache());
                eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                                socketProperties.getEventCache());
                nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                        socketProperties.getBufferPool());
    
                // Create worker collection
                if (getExecutor() == null) {
                    createExecutor();
                }
    
                initializeConnectionLatch();
    
                // Start poller threads
                pollers = new Poller[getPollerThreadCount()];
                for (int i=0; i<pollers.length; i++) {
                    pollers[i] = new Poller();
                    Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
                    pollerThread.setPriority(threadPriority);
                    pollerThread.setDaemon(true);
                    pollerThread.start();
                }
    
                startAcceptorThreads();  // ===>  这里!启动线程去接受请求!
            }
        }
    
    
      protected final void startAcceptorThreads() {
            int count = getAcceptorThreadCount();
            acceptors = new Acceptor[count];
            // 创建一堆的Acceptor,放入数组,启动它!
            for (int i = 0; i < count; i++) {
                acceptors[i] = createAcceptor();  //===>   Acceptor其实是EndPoint里的内部类。
                String threadName = getName() + "-Acceptor-" + i;
                acceptors[i].setThreadName(threadName);
                Thread t = new Thread(acceptors[i], threadName);
                t.setPriority(getAcceptorThreadPriority());
                t.setDaemon(getDaemon());
                t.start();   //  ===>   查看  NioEndpoint.Acceptor 的run方法! 这里debug是进不去的,它在另一个线程里
                // Acceptor实现了Runnable,也就是说,它是一个线程对象,启动它!
            }
        }
    
    
            @Override
            public void run() {
                // Loop until destroy() is called
                while (true) {
    
                    boolean hasEvents = false;
    
                    try {
                        if (!close) {
                            hasEvents = events();
                            if (wakeupCounter.getAndSet(-1) > 0) {
                                // If we are here, means we have other stuff to do
                                // Do a non blocking select
                                keyCount = selector.selectNow();
                            } else {
                                keyCount = selector.select(selectorTimeout);
                            }
                            wakeupCounter.set(0);
                        }
                        if (close) {
                            events();
                            timeout(0, false);
                            try {
                                selector.close();
                            } catch (IOException ioe) {
                                log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
                            }
                            break;
                        }
                    } catch (Throwable x) {
                        ExceptionUtils.handleThrowable(x);
                        log.error("",x);
                        continue;
                    }
                    // Either we timed out or we woke up, process events first
                    if (keyCount == 0) {
                        hasEvents = (hasEvents | events());
                    }
    
                    Iterator<SelectionKey> iterator =
                        keyCount > 0 ? selector.selectedKeys().iterator() : null;
                    // Walk through the collection of ready keys and dispatch
                    // any active event.
                    while (iterator != null && iterator.hasNext()) {
                        SelectionKey sk = iterator.next();
                        iterator.remove();
                        NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
                        // Attachment may be null if another thread has called
                        // cancelledKey()
                        if (socketWrapper != null) {
                            processKey(sk, socketWrapper);  // ===>  【关键节点,请求入口】请求阶段进!
                        }
                    }
    
                    // Process timeouts
                    timeout(keyCount,hasEvents);
                }
    
                getStopLatch().countDown();
            }
    

    2.3.7 tomcat的关闭

    tomcat启动后就一直处于运行状态,那么它是如何保持活动的?又是如何触发退出的?
    1)代码追踪
    1、标志位全局控制
    org.apache.catalina.startup.Bootstrap#main
    通过setAwait这个标志位来控制

     else if (command.equals("start")) {
                    daemon.setAwait(true);//用来设置主线程不自动关闭    ===>  开始调用catalina里的方法,都是反射形式
                    daemon.load(args);//===>   【重点】2、反射调用Catalina#load(args)方法,始化一些资源,优先加载conf/server.xml
                    daemon.start();//===>  【重点】3、反射调用Catalina.start()  开始启动
                    //总结
                    //Tomcat是通过调用Catalina的load()和start()方法,完成了Tomcat的启动
                    //load()方法负责组件的初始化
                    //start()方法负责组件的启动
                    if (null == daemon.getServer()) {
                        System.exit(1);
                    }
    

    2、进入到Catalina#start方法
    org.apache.catalina.startup.Catalina#start

       if (await) {
                await();   // ===>  这里留一个尾巴,【关键点!】  2.3.6的秘密就藏在这里
                stop();   // 如果 8005 接受到shutdown,上面的await会跳出,到这里 , 一堆停止和释放资源操作,忽略……
            }
    

    3、进入到await方法
    org.apache.catalina.core.StandardServer#await
    重点关注
    awaitSocket = new ServerSocket…

    @Override 
    public void await() { 
    // 监听 8005 socket 
    // 阻塞等待指令,10s超时,继续循环 
    // 收到SHUTDOWN ,退出循环 
    }
    
    

    结论:通过阻塞来实现主线程存活!
    在这里插入图片描述
    2)操作演练
    xml定义的端口 8005
    在这里插入图片描述
    将断点打在 org.apache.catalina.startup.Catalina#start, 下面的 stop() 一行

    在命令行键入:telnet ip port 后,然后键入大写的SHUTDOWN。其中port默认为8005

    在这里插入图片描述
    然后输入大写【SHUTDOWN】,会被断点捕获到。

    结论:通过使用telnet关闭8005端口也正好印证了上面的 结论。
    shutdown.bat和上面的原理也是一样的

    展开全文
  • 作为小白的我,甚是尴尬,花了很久的时间才知道tomcat源码包的下载,虽然网上有很多教程,但是大多教程对于我这个小白而言并不友好,因此对tomcat源码包的下载做记录。 1.进入Tomcat官网,官网地址:...

    作为小白的我,甚是尴尬,花了很久的时间才知道tomcat源码包的下载,虽然网上有很多教程,但是大多教程对于我这个小白而言并不友好,因此对tomcat源码包的下载做记录。

    1.进入Tomcat官网,官网地址:http://tomcat.apache.org/
    在这里插入图片描述
    2.本次演示下载的为Tomcat8.5.55
    在这里插入图片描述
    找到想要下载的版本
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    3.以上内容均为个人理解,如有错误,烦请各位大佬联系指正,非常感谢
    最后也谢谢你能看到这,如果觉得有帮助,点赞鼓励就是对我最大的支持。谢谢你~

    展开全文
  • Tomcat源码部署

    2021-10-24 16:55:22
    1、下载源码 https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.54/src/apache-tomcat-9.0.54-src.zip 2、解压 3、在解压后的根目录创建目录:catalina-home 4、复制目录到catalina-home中 5、在根目录中创建pom.xml ...
  • 在IDEA上编译运行tomcat源码将会基于Maven,因此需要先了解Maven的g、a、v概念,这样方便处理编译运行过程中可能出现的问题。g也就是Group Id,表示组织名称,a是Artifact Id,表示项目名称,v是
  • Apache Tomcat源码下载

    2021-03-29 11:11:33
    1、Tomcat下载 Tomcat官网:Tomcat官方网址,可以在此处下载Tomcat。 点击左侧Download下的对应版本。 注意有zip和exe两种格式的,zip是免安装版的,exe是安装版。同时观察自己的电脑是64位系统还是32位系统。 例如...
  • tomcat源码部署

    2021-11-19 15:00:50
    1. 源码下载 https://tomcat.apache.org/download-80.cgi 我这里下载的是tomcat8; 为其添加catalina-home 文件夹、pom.xml文件如下: catalina-home 文件夹目录如下,其中conf、wbapps是从源码文件夹src内...
  • Idea启动Tomcat源码

    2021-09-07 14:28:15
    参考: ... ... 下载tomcat源码,地址:https://tomcat.apache.org/ 解压到目录 在项目的根目录下添加pom.xml文件,内容如下: <?xml version="1.0" encoding="UTF-8...
  • tomcat源码编译步骤

    2021-01-31 23:35:02
    tomcat介绍 Tomcat是基于java语言的轻量级应用服务器,是一款完全开源免费的Servlet容器实现。支持HTML,JS等静态的处理。 tomcat8.5之后的新特性 自8.0版本开始,Tomcat支持Servlet3.1、JSP3.0、EL3.0、Websocket...
  • tomcat源码的编译还是首选ant,作为apache的顶级项目ant,可以参见下面的链接进行下载下载一个二进制运行包即可。然后就需要在path中配置了。简单的校验C:\Users\jeanron>ant -versionApache Ant(TM) version ...
  • tomcat源码部署运行

    2021-11-26 15:55:46
    tomcat官网下载地址(文中以tomcat8.5.42为例) https://tomcat.apache.org/download-80.cgi 解压zip压缩包 解压zip压缩包,进入解压目录,并创建一个目录,命名为home , 并将conf、webapps目录移入home 目录中 pom...
  • 上篇文章我们介绍了Tomcat的核心组件的的基本功能以及在Tomcat中的实现,但是这些组件类是如何被启动起来的?比如我们知道Context组件的功能是管理子容器Wraper的生命周期,那么在Context组件启动时,肯定要创建...
  • 之前使用ant成功编译了tomcat的源码(参见tomcat源码ant编译),今天想再debug一把,却发现编译失败了,如图:  很明显,ant.jar引用不到了,File -> Project Structure -> Modules -> Dependencies -> ...
  • 系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动... Tomcat的使用已经十分方便,大部分人工作中可能都不会有二次开发它的必要了。那么我们为什么还需要阅读它的源码呢? 往往熟练...
  • tomcat源码流程

    2021-07-19 18:37:24
    1.idea部署tomcat源码 1.clone项目 2.修改配置 修改build.properties.default⽂件中的base.path为当前项目目录 base.path=${basedir}/tomcat-build-libs 3.编译 1.安装ant 地址:https://ant.apache.org/ 下载:...
  • springboot支持以 jar包启动,是因为在 ioc容器启动时,同时也启动了一个 tomcat容器。 tomcat相关组件也是通过springboot的自动装配机制导入的。 入口
  • 下载Tomcat源码 apache-tomcat-9.0.41-src Idea,且装上Checkstyle插件(或者其他开发工具) 编译依赖的Jar包. 开始搭建 搭建编译环境 安装Ant,Jdk 把源码复制到...
  • #把tomcat源码包放rpmbuild/SOURCES cp tomcat-1.1.1.tar.gz rpmbuild/SOURCES #创建spec文件 vim abc.spec Name: tomcat Version: 1.1.1 Release: 1 Summary: is ok info Source0: tomc
  • 直接下载,官网提供了Binary 和 Source Code两种下载方式,要研究tomcat源代码,选择source code 方式下载方式二:利用svn获取源代码,先安装svn,再新建目录tomcat7, 接着在目录里执行以下命令svncohttp://svn....
  • 经过排查是以下这个地方加载中文配置文件时产生的乱码,原因是加载的时候中文采取的ISO-8859-1编码格式,需要改成utf-8,但是这个类是jdk的源码,没法修改,所以只能在获取的时候将String在转成utf-8格式。...
  • tomcat源码编译

    2021-07-15 09:02:22
    tomcat9:...1.下载源码包 2.解压缩 3.删除多余文件,不删也可以 4.添加pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/PO...
  • Tomcat源码下载 可以通过Tomcat下载地址下载Tomcat8.5.64版本的源码,如果需要其他版本,请自行去官网下载。 Tomcat源码编译之ant下载 Tomcat源码是使用ANT进行编译的,所以要编译Tomcat源码,就需要安装ant,...
  • 本周对 Tomcat 的多应用部署时共享公共包的能力产生了怀疑,理论上,同一个 Tomcat 下部署多个应用时,可以将所有共享 jar 放在 `shared/lib` 目录下,然后配置 `shared.loader` 就可以了。 实际实践的时候,多个 ...
  • 1. Tomcat源码下载http://tomcat.apache.org/download-90.cgi 2. IDEA导入源码导入后入下图: 2.1 导入架包Tomcat源码下下来后会少了几个架包,代码是会报错的:ant-1.6.5.jar、easymock-3.4.jar、jaxrpc-api-1.1....
  • tomcat不管是在以前的SSH框架,还是SSM,到现在的SpringBoot再到SpringCloud。都一直在使用,只是从之前的外用...所以说学习tomcat源码是有必要的。了解其内在原理,我们才能在开发和优化性能的时候才能游刃有余。
  • Tomcat 源码分析

    2021-02-25 15:38:03
    一、 Tomcat 源码分析 1 Tomcat 架构图 1.1 Server Server 服务器的意思,代表整个 tomcat 服务器,一个 tomcat 只有一个 Server Server 中包含至少一个 Service 组件,用于提供具体服务。这个在配置文件中也...
  • TOMCAT 源码环境搭建

    2021-10-08 13:38:56
    tomcat 源码下载 下载源码 git clone https://github.com/apache/tomcat.git 切换峰值 git checkout 8.5.x 修改配置 //修改配置文件build.properties base.path=/home/robin/apache/tomcat/base.path/...
  • mac 编译tomcat源码 idea

    2021-11-27 10:41:41
    tomcat 8.5 源码下载路径 解压添加pom依赖 下载解压后如图所示: 新建目录 在home目录:将conf 文件夹和 webapps 移动到home 目录下: 删除 test 文件夹。不编译测试 在bin同级目录下添加pom.xml<?xml version...
  • 最近一直在看tomcat源码,说实话看的是眼花缭乱。自己记了很多笔记,但是感觉一次上传篇幅太大。于是准备根据每一小部分来陆续上传。 在一篇先讲讲阅读tomcat的准备工作吧。 首先,你要准备一个看代码的工具,idea...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 137,103
精华内容 54,841
关键字:

tomcat源码下载