博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
tomcat架构分析(valve源码导读)
阅读量:6557 次
发布时间:2019-06-24

本文共 8125 字,大约阅读时间需要 27 分钟。

出处: 

源码面前,了无秘密 
                             ----侯捷 
在tomcat架构分析(valve机制)()里已经对valve的机制做了分析。现在通过源码来加深下理解。侯捷说过,源码面前,了无秘密。通过这些代码,可以看到在tomcat中我们经常碰到的一些现象或配置是怎么实现的。 
StandardEngineValve
看一下StandardEngineValve的调用逻辑; 

Java代码

public final void invoke(Request request, Response response)      throws IOException, ServletException {        // 定位host      Host host = request.getHost();      if (host == null) {          ......          return;      }        // 调用host的第一个valve      host.getPipeline().getFirst().invoke(request, response);    }

 

可以清晰的看到,根据request定位到可以处理的host对象,同时,开始从头调用host里的pipeline上的valve。 

StandardHostValve 
看一下StandardHostValve的调用逻辑; 

Java代码
public final void invoke(Request request, Response response)      throws IOException, ServletException {        // 定位context      Context context = request.getContext();      if (context == null) {          ......          return;      }        ......        // 调用context的第一个valve      context.getPipeline().getFirst().invoke(request, response);        // 更新session      if (Globals.STRICT_SERVLET_COMPLIANCE) {          request.getSession(false);      }        // Error page processing      response.setSuspended(false);        //如果有抛异常或某个HTTP错误,导向响应的配置页面      Throwable t = (Throwable) request.getAttribute(Globals.EXCEPTION_ATTR);        if (t != null) {          throwable(request, response, t);      } else {          status(request, response);      }        // Restore the context classloader      Thread.currentThread().setContextClassLoader          (StandardHostValve.class.getClassLoader());    }

 

可以清晰的看到,注释部分里根据request定位到可以处理的context对象,同时,开始从头调用context里的pipeline上的valve。在调用完context的所有的valve之后(当然也是context调用完其对应的wrapper上的所有valve之后),蓝色部分显示了拿到response对象时可以做的处理。 

熟悉tomcat的可能有配置错误信息的经验,例如; 

Xml代码
404
/error.jsp

 

它就是为了在用户访问资源出现HTTP 404错误时,将访问重定向到一个统一的错误页面。这样做一是为了美观,另一个主要作用是不会将一些具体的错误信息例如java抛异常时的栈信息暴露给用户,主要还是出于安全的考虑。 上述代码中的注释部分就是实现这个重定向功能。 

StandardContextValve 
看一下StandardContextValve的调用逻辑;其代码比较多,只贴一些比较核心的吧。 

Java代码
public final void invoke(Request request, Response response)      throws IOException, ServletException {        ......        // 定位wrapper      Wrapper wrapper = request.getWrapper();      if (wrapper == null) {          notFound(response);          return;      } else if (wrapper.isUnavailable()) {          ......      }        // Normal request processing      //web.xml中配置web-app/listener/listener-class      Object instances[] = context.getApplicationEventListeners();        ServletRequestEvent event = null;        //响应request初始化事件,具体的响应listener是可配置的       ......      //调用wrapper的第一个valve      wrapper.getPipeline().getFirst().invoke(request, response);         //响应request撤销事件,具体的响应listener是可配置的       ......               }

 

可以清晰的看到,注释部分里根据request定位到可以处理的wrapper对象,同时,开始从头调用wrapper里的pipeline上的valve。 需要注意的是,这里在调用wrapper的valve前后,分别有响应request初始化及撤销事件的逻辑,tomcat有一整套事件触发体系,这里限于篇幅就不阐述了。有时间专门说。 

StandardWrapperValve 
看一下StandardWrapperValve的调用逻辑;其代码比较多,只贴一些比较核心的吧; 

Java代码
public final void invoke(Request request, Response response)      throws IOException, ServletException {            ......      requestCount++;      //定位wrapper      StandardWrapper wrapper = (StandardWrapper) getContainer();      Servlet servlet = null;      Context context = (Context) wrapper.getParent();            ......        // Allocate a servlet instance to process this request      try {          if (!unavailable) {              //加载servlet              servlet = wrapper.allocate();                          }      } catch (UnavailableException e) {          ......      }       ......      // 根据配置建立一个filter-servlet的处理链表,servlet在链表的尾端      ApplicationFilterFactory factory =          ApplicationFilterFactory.getInstance();      ApplicationFilterChain filterChain =          factory.createFilterChain(request, wrapper, servlet);      // Reset comet flag value after creating the filter chain      request.setComet(false);        // Call the filter chain for this request      // NOTE: This also calls the servlet's service() method      try {          String jspFile = wrapper.getJspFile();          if (jspFile != null)              request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);          else              request.removeAttribute(Globals.JSP_FILE_ATTR);          if ((servlet != null) && (filterChain != null)) {              // Swallow output if needed              if (context.getSwallowOutput()) {                  try {                      SystemLogHandler.startCapture();                      if (comet) {                          filterChain.doFilterEvent(request.getEvent());                          request.setComet(true);                      } else {                          //调用filter-servlet链表                          filterChain.doFilter(request.getRequest(),                                   response.getResponse());                      }                  } finally {                      String log = SystemLogHandler.stopCapture();                      if (log != null && log.length() > 0) {                          context.getLogger().info(log);                      }                  }              } else {                  if (comet) {                      request.setComet(true);                      filterChain.doFilterEvent(request.getEvent());                  } else {                      //调用filter-servlet链表                      filterChain.doFilter                          (request.getRequest(), response.getResponse());                  }              }            }          request.removeAttribute(Globals.JSP_FILE_ATTR);      } catch (ClientAbortException e) {          request.removeAttribute(Globals.JSP_FILE_ATTR);          throwable = e;          exception(request, response, e);      }       ......  }

 

可以清晰的看到,注释部分里,先是能拿到相应的wrapper对象;然后完成加载wrapper对象中的servlet,例如如果是jsp,将完成jsp编译,然后加载servlet等;再然后,根据配置生成一个filter栈,通过执行栈,调用完所有的filter之后,就调用servlet,如果没有配置filter,就直接调用servlet,生成filter栈是通过request的URL模式匹配及servlet名称来实现的,具体涉及的东西在tomcat的servlet规范实现中再阐述吧。 

以上,完成了一整套servlet调用的过程。通过上面的阐述,可以看见valve是个很灵活的机制,通过它可以实现很大的扩展。 
Valve的应用及定制化 
Tomcat除了提供上面提到的几个标准的valve实现外,也提供了一些用于调试程序的valve的实现。实现valve需要继承org.apache.catalina.valves.ValveBase基类。 以RequestDumperValve为例, 

引用
org.apache.catalina.valves.RequestDumperValve

 

RequestDumperValve是打印出request及response信息的valve。其实现方法为: 

Java代码
public void invoke(Request request, Response response)                   throws IOException, ServletException {             Log log = container.getLogger();             // Log pre-service information           log.info("REQUEST URI =" + request.getRequestURI());           ......           log.info(" queryString=" + request.getQueryString());           ......           log.info("-------------------------------------------------------");                     // 调用下一个valve          getNext().invoke(request, response);                     // Log post-service information           log.info("-------------------------------------------------------");           ......           log.info(" contentType=" + response.getContentType());           Cookie rcookies[] = response.getCookies();           for (int i = 0; i < rcookies.length; i++) {               log.info(" cookie=" + rcookies[i].getName() + "=" +                   rcookies[i].getValue() + "; domain=" +                   rcookies[i].getDomain() + "; path=" + rcookies[i].getPath());           }           String rhnames[] = response.getHeaderNames();           for (int i = 0; i < rhnames.length; i++) {               String rhvalues[] = response.getHeaderValues(rhnames[i]);               for (int j = 0; j < rhvalues.length; j++)               log.info(" header=" + rhnames[i] + "=" + rhvalues[j]);                    }          log.info(" message=" + response.getMessage());           log.info("========================================================");     }

 

可以很清晰的看出,它打印出了request及response的信息,其中红色部分显示它调用valve链表中的下一个valve。我们可以这样配置它; 

Xml代码

 

这样,只要访问此host下的所有context,都会打印出调试信息。 Valve的应用有很多,例如cluster,SSO等,会有专门一章来讲讲。

转载地址:http://jzhco.baihongyu.com/

你可能感兴趣的文章
钓鱼网站-现代网络“李鬼”
查看>>
mac/Linux源码安装TensorFlow
查看>>
动态排序JavaBean
查看>>
26 计算用户输入的内容中索引为奇数并且对应的元素为数字的个数的两种方法...
查看>>
iOS 色值 转换 干货
查看>>
利用Python实现12306爬虫--查票
查看>>
day7——Python的帮助
查看>>
js-键盘回车搜索enter
查看>>
学习编译原理的心得
查看>>
安装pip和pylint
查看>>
POJ 3974 Palindrome
查看>>
fix issue on ImportError: libGeoIP.so.1
查看>>
linear-gradient线性渐变
查看>>
POJ 2965 The Pilots Brothers' refrigerator【枚举】
查看>>
我的天$删除注册表$安装mysql最后一步不能启动服务的解决办法
查看>>
Set Matrix Zeroes
查看>>
循环——批量处理数据
查看>>
高精度双目立体视觉测量
查看>>
Flatty Shadow图标自动产生器——在线生成各种扁平化 ICON
查看>>
org.tinygroup.beancontainer-IOC、AOP框架
查看>>