当前位置: 首页 > news >正文

tomcat-valve通过servlet处理请求

上一节说到请求url定位servlet的过程,tomcat会把请求url和容器的映射关系保存到MappingData中,org.apache.catalina.connector.Request类实现了HttpServletRequest,其中定义了属性mappingDataprotected final MappingData mappingData = new MappingData();用于保存映射关系,其是在CoyoteAdapter.java#service()方法中创建。定位servlet对请求进行处理的入口是connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);,先看张图总览下处理过程。
在这里插入图片描述

这里的逻辑就是调用Pipeline中的每个Valve进行处理。在ContainerBase.java抽象父类中声明了属性protected final Pipeline pipeline = new StandardPipeline(this);,意味着ContainerBase的子类都会有一个管道Pipeline(StandardPipeline),查看类的继承关系可知,tomcat中ContainerBase的子类有四个:StandardEngineStandardHostStandardContextStandardWrapper
在这里插入图片描述

  • StandardEngine
    通过connector.getService().getContainer().getPipeline()获取到Engine(StandardEngine)容器,再通过getPipeline().getFirst()获取Pipeline(StandardPipeline.java)中的第一个Valve,即使用StandardEngine中的StandardPipeline的第一个Valve进行处理,接下来看下这个Valve怎么来的。在Valve中有个属性protected Valve next = null;,用来指向下一个Valve,这就构建了一个链表,getFirst()时发现链条为空,则默认获取basic,而StandardEngine的pipeline中的basic是在StandardEngine的构造函数中生成的。
    public Valve getFirst() {if (first != null) {return first;}return basic;}public StandardEngine() {pipeline.setBasic(new StandardEngineValve());......}public void setBasic(Valve valve) {
......Valve current = first;while (current != null) {if (current.getNext() == oldBasic) {current.setNext(valve);break;}current = current.getNext();}this.basic = valve;}

搞清楚了这个Valve的由来了,再看他的处理逻辑:就是找到前面通过请求url定位的Host,获取Host的Valve进行处理。

    public void invoke(Request request, Response response) throws IOException, ServletException {// Select the Host to be used for this RequestHost host = request.getHost();......host.getPipeline().getFirst().invoke(request, response);}public Host getHost() {return mappingData.host;}
}
  • StandardHost
    host.getPipeline().getFirst().invoke(request, response);的过程类似类似StandardEngine容器。
    public StandardHost() {super();pipeline.setBasic(new StandardHostValve());}

不过Host容器的Valve要多一点。首先在server.xml中配置了一个AccessLogValve,这个在使用Digester解析时会添加到Host容器的Pipeline,既然是解析xml文件生成,那我们就可以在xml文件中自定义一些Valve了。

    <Host name="localhost"  appBase="webapps"unpackWARs="true" autoDeploy="true"><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>

同时在启动时会向Pipeline添加一个ErrorReportValve

    protected void startInternal() throws LifecycleException {// Set error report valveString errorValve = getErrorReportValveClass();if ((errorValve != null) && (!errorValve.equals(""))) {try {boolean found = false;Valve[] valves = getPipeline().getValves();for (Valve valve : valves) {if (errorValve.equals(valve.getClass().getName())) {found = true;break;}}if (!found) {Valve valve = ErrorReportValve.class.getName().equals(errorValve) ? new ErrorReportValve() :(Valve) Class.forName(errorValve).getConstructor().newInstance();getPipeline().addValve(valve);}} catch (Throwable t) {ExceptionUtils.handleThrowable(t);log.error(sm.getString("standardHost.invalidErrorReportValveClass", errorValve), t);}}super.startInternal();}

最终tomcat收到请求使用Host容器处理时,就会有三个Valve参与:AccessLogValve->ErrorReportValve->StandardHostValve。
接下来我们一个个看每个Valve的处理过程。

  1. AccessLogValve
    好像没做什么正事,之后再看。
    public void invoke(Request request, Response response) throws IOException, ServletException {if (tlsAttributeRequired) {request.getAttribute(Globals.CERTIFICATES_ATTR);}if (cachedElements != null) {for (CachedElement element : cachedElements) {element.cache(request);}}getNext().invoke(request, response);}
  1. ErrorReportValve.java
    这是在请求处理完的后置处理,用来报告错误的。
    public void invoke(Request request, Response response) throws IOException, ServletException {// Perform the requestgetNext().invoke(request, response);if (response.isCommitted()) {if (response.setErrorReported()) {// Error wasn't previously reported but we can't write an error// page because the response has already been committed.// See if IO is allowedAtomicBoolean ioAllowed = new AtomicBoolean(true);response.getCoyoteResponse().action(ActionCode.IS_IO_ALLOWED, ioAllowed);if (ioAllowed.get()) {// I/O is currently still allowed. Flush any data that is// still to be written to the client.try {response.flushBuffer();} catch (Throwable t) {ExceptionUtils.handleThrowable(t);}// Now close immediately to signal to the client that// something went wrongresponse.getCoyoteResponse().action(ActionCode.CLOSE_NOW,request.getAttribute(RequestDispatcher.ERROR_EXCEPTION));}}return;}Throwable throwable = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);// If an async request is in progress and is not going to end once this// container thread finishes, do not process any error page here.if (request.isAsync() && !request.isAsyncCompleting()) {return;}if (throwable != null && !response.isError()) {// Make sure that the necessary methods have been called on the// response. (It is possible a component may just have set the// Throwable. Tomcat won't do that but other components might.)// These are safe to call at this point as we know that the response// has not been committed.response.reset();response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);}// One way or another, response.sendError() will have been called before// execution reaches this point and suspended the response. Need to// reverse that so this valve can write to the response.response.setSuspended(false);try {report(request, response, throwable);} catch (Throwable tt) {ExceptionUtils.handleThrowable(tt);}}
  1. StandardHostValve
    没有大的处理,还是把请求向后传递到Context容器的Valve。
public void invoke(Request request, Response response) throws IOException, ServletException {Context context = request.getContext();......context.getPipeline().getFirst().invoke(request, response);......
}
  • StandardContext
    在tomcat启动时,StandardContext#startInternal()方法会添加一个NonLoginAuthenticator类型Valve到StandardContext的Pipeline中,然后设置basic(StandardContextValve)。StandardContextValve从请求中获取StandardWrapperValve进行请求的处理。
  • StandardWrapperValve
    前面铺垫了这么久,总算到正题了,请求的正式处理就是在StandardWrapperValve中进行的,前面的Valve做的还是对请求进行加工,没有进行正式的处理。怎么样,是不是看到熟悉的代码段,平时我们所说的过滤器链对请求过滤处理的代码!这块就是挨个调用每个过滤器Filter上的doFilter()方法进行处理。
    public void invoke(Request request, Response response) throws IOException, ServletException {......Servlet servlet = null;......servlet = wrapper.allocate();ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);......filterChain.doFilter(request.getRequest(), response.getResponse());......        }

还是慢慢看吧,首先在Wrapper容器中取出Servlet,然后创建用来处理请求的servlet过滤器链。依次调用每个过滤器的doFilter方法,最后会通过HttpServlet中的模板方法internalDoFilter()调用内置的service方法,就是根据请求方式处理请求啦,也就是我们写的过滤器中处理请求的方法。

    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String method = req.getMethod();if (method.equals(METHOD_GET)) {long lastModified = getLastModified(req);if (lastModified == -1) {// servlet doesn't support if-modified-since, no reason// to go through further expensive logicdoGet(req, resp);} else {long ifModifiedSince;try {ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);} catch (IllegalArgumentException iae) {// Invalid date header - proceed as if none was setifModifiedSince = -1;}if (ifModifiedSince < (lastModified / 1000 * 1000)) {// If the servlet mod time is later, call doGet()// Round down to the nearest second for a proper compare// A ifModifiedSince of -1 will always be lessmaybeSetLastModified(resp, lastModified);doGet(req, resp);} else {resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);}}} else if (method.equals(METHOD_HEAD)) {long lastModified = getLastModified(req);maybeSetLastModified(resp, lastModified);doHead(req, resp);} else if (method.equals(METHOD_POST)) {doPost(req, resp);} else if (method.equals(METHOD_PUT)) {doPut(req, resp);} else if (method.equals(METHOD_DELETE)) {doDelete(req, resp);} else if (method.equals(METHOD_OPTIONS)) {doOptions(req, resp);} else if (method.equals(METHOD_TRACE)) {doTrace(req, resp);} else {//// Note that this means NO servlet supports whatever// method was requested, anywhere on this server.//String errMsg = lStrings.getString("http.method_not_implemented");Object[] errArgs = new Object[1];errArgs[0] = method;errMsg = MessageFormat.format(errMsg, errArgs);resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);}}

总结下,tomcat使用valve处理请求是请求处理的最后一步。首先启动时会给每个容器的Pipeline添加一个基本valve,放在链条尾部,由这个valve找到下一个容器的valve,用来传递请求,最终传递到StandardWrapperValve后,取出servlet对请求进行处理。有不对的地方请大神指出,欢迎大家一起讨论交流,共同进步。

相关文章:

  • HTTPS和TCP
  • C++ 数据共享与保护学习记录【代码】
  • Unity 编辑器扩展 一键替换指定物体下的所有材质球
  • Android14 WMS-窗口绘制之relayoutWindow流程(一)-Client端
  • Java学习-JDBC(一)
  • 【数据结构】图论入门
  • 开发常用软件
  • PDF编辑与转换的终极工具智能PDF处理Acrobat Pro DC
  • Day14:响应式网页
  • java 原生http服务器 测试JS前端ajax访问实现跨域传post数据
  • 【Python爬虫单点登录实战】PyExecJS破解慧职教:过河源技术学院单点登录统一身份认证
  • 电脑开机出现英文字母,如何解决这个常见问题?
  • MAVEN:自定义模板Archetype的创建
  • 【java】速度搭建一个springboot项目
  • 计算机网络--应用层
  • hexo+github搭建个人博客
  • [原]深入对比数据科学工具箱:Python和R 非结构化数据的结构化
  • Android开源项目规范总结
  • github从入门到放弃(1)
  • gulp 教程
  • js正则,这点儿就够用了
  • Laravel5.4 Queues队列学习
  • WebSocket使用
  • 阿里云容器服务区块链解决方案全新升级 支持Hyperledger Fabric v1.1
  • 第十八天-企业应用架构模式-基本模式
  • 基于遗传算法的优化问题求解
  • 开放才能进步!Angular和Wijmo一起走过的日子
  • 吐槽Javascript系列二:数组中的splice和slice方法
  • 正则与JS中的正则
  • 函数计算新功能-----支持C#函数
  • #pragma 指令
  • (007)XHTML文档之标题——h1~h6
  • (12)Hive调优——count distinct去重优化
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (九)c52学习之旅-定时器
  • (每日持续更新)jdk api之FileFilter基础、应用、实战
  • (深度全面解析)ChatGPT的重大更新给创业者带来了哪些红利机会
  • (限时免费)震惊!流落人间的haproxy宝典被找到了!一切玄妙尽在此处!
  • (一)spring cloud微服务分布式云架构 - Spring Cloud简介
  • (原創) 如何讓IE7按第二次Ctrl + Tab時,回到原來的索引標籤? (Web) (IE) (OS) (Windows)...
  • (转)iOS字体
  • .gitignore文件使用
  • .MyFile@waifu.club.wis.mkp勒索病毒数据怎么处理|数据解密恢复
  • .NET 8.0 发布到 IIS
  • .NET Conf 2023 回顾 – 庆祝社区、创新和 .NET 8 的发布
  • .net 按比例显示图片的缩略图
  • .NET 药厂业务系统 CPU爆高分析
  • .Net转Java自学之路—SpringMVC框架篇六(异常处理)
  • @property @synthesize @dynamic 及相关属性作用探究
  • [000-01-011].第2节:持久层方案的对比
  • [1159]adb判断手机屏幕状态并点亮屏幕
  • [240812] X-CMD 发布 v0.4.5:更新 gtb、cd、chat、hashdir 模块功能
  • [C/C++随笔] char与unsigned char区别
  • [C++初阶]list的模拟实现
  • [FPGA]-时序传输模型分析