主要分四部分: 功能是从需求的角度进行描述的,架构是从设计的角度进行描述的,源码是从实现的角度进行描述的 什么是Tomcat架构: 为了实现上述的功能,Tomcat进行了很多的封装设计,封装出了很多的组件(组件在源代码中的体现就是Java类),组件与组件之间的关系就构成了所谓的Tomcat架构。 除了Connector组件和Container组件,Tomcat其实还定义了很多其他组件来工作(server-service-connector/container-engine-host-context-wrapper)。这些组件采用一层套一层的设计方式(套娃式),如果一个组件包含了其他组件,那么这个组件也称之为容器。 conf目录下的server.xml文件: 剖析源代码需要讲究一些原则,注意一些方法和技巧,否则很容易就在浩瀚的源代码海洋中迷失自己 好处:提高我们的架构思维、深入认识代码、深入理解一个项目/框架 原则: 方法和技巧 说明: 操作步骤: 步骤一:解压源码压缩包,得到目录apache-tomcat-8.5.54-src 下载地址:https://tomcat.apache.org/download-80.cgi 下载最下面的Source Code Distributions源码版本,我这里下载的是zip的 步骤二:进入apache-tomcat-8.5.54-src目录,创建一个pom.xml文件,文件内容如下: 步骤三:在apache-tomcat-8.5.54-src目录中创建source文件夹 步骤四:将conf、webapps目录移动到刚刚创建的source文件夹中 步骤五:将源码工程导入到IDEA中 IDEA中应当已经配置好Maven 步骤六:给tomcat的源码程序配置Run/Debug Configurations 注意这里要使用Java11,因为pom.xml中配置使用的是Java11 这里要给tomcat的源码程序启动类Bootstrap配置VM参数,因为tomcat源码运行也需要加载配置文件等 步骤七:Build项目,此时会报错 Tomcat要启动,肯定要把架构中提到的组件进行实例化(实例化创建–>销毁等:生命周期)。Tomcat中那么多组件,为了统一规范他们的生命周期,Tomcat抽象出了LifeCycle生命周期接口 LifeCycle生命周期接口方法: (1)启动入口分析 startup.sh –> catalina.sh start –> java xxxx.jar org.apache.catalina.startup.Bootstrap(main) start(参数) (2)启动流程图 ① Bootstrap的main方法: catalinaDaemon = catalina对象 daemon = bootstrap对象 ③ Bootstrap的load方法: (1)流程图 ① Catalina的load方法: Server的init初始化: ② Digester的parse方法: 得到的root变量内容: server相关值: service相关值: engine相关值: 得到的内容与解析的server.xml文件配置内容一致 ③ LifecycleBase的init方法(使用了设计模式的模板方法): 此处的initInternal会跳转至实现该接口的具体类的对象中 ④ StandardServer的initInternal方法: 会跳转至service的init方法中,即继续调用service实现的Lifecycle的init方法 ⑤ StandardService的initInternal方法: 分别初始化service中的容器部分和connector部分 ⑥ StandardEngine的initInternal方法: ⑦ ContainerBase的initInternal方法: 注:ContainerBase是StandardEngine、StandardWrapper、StandardContext和StandardHost的父类 ⑧ Connector的initInternal方法: 绑定适配器: 初始化protocolHandler组件: ⑨ AbstractProtocol的init方法: ⑩ AbstractEndpoint的init方法: ⑪ NioEndpoint的bind方法: (1)流程图 与load过程很相似 (2)源码分析 ① Catalina的start方法: ② LifecycleBase的start方法(同样使用了设计模式的模板方法): ④ StandardServer的startInternal方法: ⑤ StandardService的startInternal方法: 分别启动其中的engine和connector ① StandardEngine的startInternal方法: 最后调用父类ContainerBase的startInternal方法 ② ContainerBase的startInternal方法: Engine调用它父类执行该方法时,子容器为Host: 将子容器Host提交到具体的StartChild线程类并行执行 注:该线程池只专门用来实例化Host Host调用它父类执行该方法时,通过设置生命周期事件来进行实例化 ③ StartChild线程类: ④ StandardHost的startInternal方法: ⑤ LifecycleBase的setStateInternal方法: ⑥ LifecycleBase的fireLifecycleEvent方法: 触发Host的生命周期事件后,将后续工作交给生命周期监听器HostConfig来进行 ⑦ Hostconfig的lifecycleEvent方法: 捕获start事件,执行start方法: ⑧ Hostconfig的start方法: ⑨ Hostconfig的deployApps方法: 根据不同的应用部署方式,调用不同的方法 ⑩ Hostconfig的deployDirectories方法: 以线程方式并行处理多个项目: ⑪ DeployDirectory线程类: ⑫ Hostconfig的deployDirectory方法: 通过xml解析对象进行分析: 设置一些context应用的必要属性: 完善context的过程在addChild方法中 ⑬ StandardHost的addChild方法: ⑭ ContainerBase的addChild方法: ⑮ ContainerBase的addChildInternal方法: ⑯ StandardContext的startInternal方法: work目录存放jsp转换为servlet的中间过程临时文件 给每个应用设置类加载器: 查看已初始化的Servlet,其中两个为tomcat默认的servlet,另一个resumeservlet为自己的项目web_demo中的servlet: 由上图看出,此时只读取了servlet对应的类,但并未生成实例化对象。 loadOnStartup方法根据web.xml中配置servlet的load-on-startup来进行创建实例化对应servlet。执行之后,instance就有具体对象了。 load-on-startup大于0时在容器启动时加载,否则在第一次访问该servlet时加载 ⑰ StandardContext的loadOnStartup方法: ⑱ StandardWrapper的load方法: ⑲ StandardWrapper的loadServlet方法(实际实例化servlet的方法): ① Connector的startInternal方法: ② AbstractProtocol的start方法: ③ AbstractEndpoint的start方法: ④ NioEndpoint的startInternal方法: Tomcat中的NIO模型: (1) 获取请求的工作封装交给了Acceptor线程去完成 (2) poller线程(下面分析servlet请求处理时的入口) 检查selector中是否有数据到来的channel,如果有就要进行处理 ⑤ AbstractEndpoint的startAcceptorThreads方法: ⑥ NioEndpoint的createAcceptor方法: ⑦ Acceptor线程类中的run方法: 一个servlet请求 –> 最终需要找到能够处理当前servlet请求的servlet实例 –> servlet.service() (1)结构图 Tomcat中使用Mapper机制重新封装了Host-context-wrapper(servlet)之间的数据和关系。 在匹配出能够处理当前请求的对应Host、对应Context和对应Wrapper之前,mapper对象肯定已经初始化好了 (2)源代码结构 ① Mapper类中有MappedHost数组,表示有多个Host ② MappedHost中有一个ContextList ③ ContextList类中有MappedContext数组,表示该Host有多个Context ④ MappedContext中有一个ContextVersion数组 ⑤ ContextVersion中的MappedWrapper数组对应的就是servlet ⑥ MappedHost、MappedContext和MappedWrapper都有一个MapElement基类 StandardService –> startInternal –> mapperListener.start()中完成mapper对象初始化 (1)StandardService的startInternal方法: 基于已有信息数据完成Mapper对象的初始化 此处的mapperListener也遵从统一的生命周期管理 (2)MapperListener的startInternal方法: (3)MapperListener的registerHost方法: 此时的变量情况: (1)基本流程 (2)详细流程 Poller线程是追踪入口 (1) Poller的run方法: (2)Poller的processKey方法: 在processSocket处设置断点: (3)启动服务器,从浏览器发起访问请求 (4)AdstractEndpoint的processSocket方法: 把传入的socket交给一个线程进行处理 (5)线程类SocketProcessorBase的run方法: (6)SocketProcessor的doRun方法: 此处的getHandler为中间过程对象 (7)ConnectionHandler的process方法: 取不到对应Processor的话 仍没有则创建一个新的 找到完成之后,使用其进行process 此时的processor为Http11Processor,因为默认的http协议是1.1版本。它是用来解析Socket中的请求信息的 (8)AbstractProcessorLight的process方法: (9)Http11Processor的service方法: 此时获取得到的是CoyoteAdapter,连接器组件适配器 (10)CoyoteAdapter的service方法: 传入的参数req和res是原生的Request和Response,第一步先进行适配转换 转换完成后交给后面的Container中的具体组件处理,一层一层找到对应servlet,该流程对应了postParseRequest方法,即利用Mapper进行匹配查找,下面的(11)-(13)说明了这一过程 匹配完成后调用下面的invoke逐级调用匹配的结果,下面的(14)-(22)说明了这一过程 (11)CoyoteAdapter的postParseRequest方法: (12)Mapper的map方法: (13)Mapper的internalMap方法进行具体的相关匹配: 分别根据name匹配host、context和mapper 匹配到的结果存放在参数mappingData中(mappingDate就在request对象中): (14)StandardEngineValve的invoke方法: (15)AbstractAccessLogValve的invoke方法: (16)ErrorReportValve的invoke方法: (17)StandardHostValve的invoke方法: Request的gerContext方法: 继续深入调用context (18)AuthenticatorBase的invoke方法: (19)StandardContextValve的invoke方法: Request的getWrapper方法: (20)StandardWrapperValve的invoke方法: 可以看到,执行完成后,得到的servlet即为对应的servlet (21)ApplicationFilterChain的doFilter方法: (22)ApplicationFilterChain的internalDoFilter方法: 此处是最终执行servlet中实际操作的部分!! 自己编写的ResumeServlet的内容
一、Tomcat架构设计
1.1 Tomcat的功能(需求)
Tomcat两个非常重要的功能(身份)
1.2 Tomcat的架构(架构就是为了完成功能需求做的设计)
1.2.1 架构解读
一个Service内部可以有多个Connector组件,因为一个Connector绑定一个端口进行监听,多个Connector可以监听多个端口,但是一个Service内部的多个Connector只能对应一个Servlet容器。1.2.2 架构与配置文件的对应
1.3 Tomcat套娃式架构设计的好处
二、源码剖析技巧
三、Tomcat实例构建脉络
3.1 源码构建方式
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.apache.tomcat</groupId> <artifactId>apache-tomcat-8.5.50-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是java开发的,封装了很多功能,它需要依赖一些基础的jar包--> <dependencies> <!--远程过程调用工具包--> <dependency> <groupId>javax.xml</groupId> <artifactId>jaxrpc</artifactId> <version>1.1</version> </dependency> <!--soap协议处理工具包--> <dependency> <groupId>javax.xml.soap</groupId> <artifactId>javax.xml.soap-api</artifactId> <version>1.4.0</version> </dependency> <!--解析webservice的wsdl文件工具--> <dependency> <groupId>wsdl4j</groupId> <artifactId>wsdl4j</artifactId> <version>1.6.2</version> </dependency> <!--Eclipse Java编译器--> <dependency> <groupId>org.eclipse.jdt.core.compiler</groupId> <artifactId>ecj</artifactId> <version>4.5.1</version> </dependency> <!--ant管理工具--> <dependency> <groupId>ant</groupId> <artifactId>ant</artifactId> <version>1.7.0</version> </dependency> <!---easymock辅助单元测试--> <dependency> <groupId>org.easymock</groupId> <artifactId>easymock</artifactId> <version>3.4</version> </dependency> </dependencies> </project>
-Dcatalina.home=E:CodeIdeaProjectsapache-tomcat-8.5.54-src/source -Dcatalina.base=E:CodeIdeaProjectsapache-tomcat-8.5.54-src/source -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file=E:CodeIdeaProjectsapache-tomcat-8.5.54-src/source/conf/logging.properties
填写VM参数时根据自己tomcat源码项目放置位置进行更改
找到错误位置,使用Alt+Enter第一条即可修复该问题,然后重新Build即可
步骤八:运行项目就启动了tomcat,启动时会加载所配置的conf目录下的server.xml等配置文件,所以访问8080端口即可,但此时会遇到如下的一个错误:
原因是Tomcat源码中Jsp引擎Jasper没有被初始化,从而无法编译处理Jsp(因为Jsp是需要被转换成servlet进一步编译处理的),只需要在tomcat的源码ContextConfig类中的configureStart方法中增加一行代码将Jsp引擎初始化,如下
步骤九:重启Tomcat,正常访问即可。至此,Tomcat源码构建完毕。3.2 启动过程源码剖析
3.2.1 生命周期统一管理组件LifeCycle
LifeCycle生命周期接口的继承体系:
3.2.2 启动流程
(3)启动流程分析
② Bootstrap的init方法:
经过前两步,设置了
④ Bootstrap的start方法:
3.2.3 load初始化阶段
(2)源码分析
3.2.4 start启动阶段
3.2.4.1 Engine启动过程start源码分析
关键步骤(把具体每个应用的处理交给了ContextConfig):3.2.4.2 Connector启动过程start源码分析
四、Servlet请求处理链路
4.1 Mapper组件体系结构
4.2 mapper对象数据何时初始化的
4.3 Servlet请求处理流程示意
4.4 Servlet请求处理源码剖析
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算