暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

熬夜读懂Tomcat源码(五)

IT路人乙 2020-03-07
344

之前理清楚了Servlet的初始化过程,这里我开始追踪Servlet service()方法的调用过程。至于为什么要搞清楚Servlet service()方法的调用,其实原因很简单:熟悉Java Web开发的人都知道,当Tomcat启动时会调用一次Servlet的init()方法;当客户端发起请求时,Tomcat会调用Servlet的service()方法,在该方法中会根据HTTP请求的类型将请求转发到doGet()方法、doPost()方法、doHead()方法、doPut()方法、doDelete()方法、doOptions()方法和doTrace()方法中。因此,搞清楚Tomcat调用Servlet的service()流程至关重要。

1 Tomcat怎样调用Servlet的service方法处理请求?

上一篇文章我们知道在StandardWrapperValve实例的invok()方法将Servlet的init()初始化方法调用委托给了StandardWrapper实例的allocate()方法完成。那么,当Servlet初始化完成之后,Servlet实例又被如何处置呢?进入到StandardWrapperValve实例的invoke()方法中,我发现下面的处理片段

public final void invoke(Request request, Response response)
throws IOException, ServletException {

// Allocate a servlet instance to process this request
try {
  if (!unavailable) {
  // 实例化Servlet,并调用Servlet实例的init()方法初始化Servlet
servlet = wrapper.allocate();
  }
}

// 为请求创建过滤器链
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

// Call the filter chain for this request
// NOTE: This also calls the servlet's service() method
try {
// 将请求委托给ApplicationFilterChain的doFilter()方法处理
  filterChain.doFilter
  (request.getRequest(), response.getResponse());
}
}

如代码注释,这里将Servlet实例和Request请求委托给了ApplicationFilterChain过滤器链处理。过滤器链很大的优点就是如果不看清楚下一个过滤器是谁容易将人绕晕。这让我想起之前做的一个项目,平台为了统一编码格式,统一将请求封装为Action,每个请求的参数都需要携带一个action=xxx的参数作为请求转发标志,而根据action请求转发的操作就使用过滤器(^▽^)。我记得当时是写了两个过滤器,第一个过滤器进行权限校验,获取用户信息放到Session中;第二个过滤器处理根据action参数请求转发到具体的Action;两个过滤器组装成了一个过滤器链。下面我们就进入ApplicationFilterChain过滤器链的doFilter()方法一探究竟。

public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
...
// 调用私有方法internalDoFilter()
internalDoFilter(request,response);
}
...
private void internalDoFilter(ServletRequest request,
                                ServletResponse response)
throws IOException, ServletException {

// We fell off the end of the chain -- call the servlet instance
try {
// 调用Servlet实例的service()方法
  servlet.service(request, response);
}
}

如上面关键代码片段,ApplicationFilterChain实例在internalDoFilter()方法中调用了Servlet的service()方法。就这样,我们就从Tomcat来到了servlet-api约定的Servlet的service()方法了,整个流程也就结束了。

2 应该怎么定制Servlet?

在日常开发过程中,通常情况下,我们自定义的Servlet会继承servlet-api提供的HttpServlet抽象类,该类默认实现了service()方法,在方法中完成根据HTTP请求类型将请求转发到对应的处理方法中

protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {

String method = req.getMethod();
if (method.equals(METHOD_GET)) {
  doGet(req, resp);
} else if (method.equals(METHOD_HEAD)) {
  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 {
  resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}

因此,在我们定制的Servlet中就不需要再实现service()方法了。当然,如果你认为这样的转发还满足不了你的需求,或者说你认为使用if-else语句不满意,你也可以覆盖service()方法实现自己的逻辑。

最近在看三国演义的火烧赤壁,以诸葛亮、庞统为首的地下间谍组织给曹操做了一个局,曹老板作为局外人,也看都不看的就往坑里跳,最终奠定了蜀魏吴三足鼎立的局势(^▽^)。曹老板才真是传说中的大心脏!曹营的徐庶算一个明白人,可惜心在皇叔,不愿点破。曹老板的教训告诫我们,日常工作生活中,你得罪谁都不能得罪明白人!Tomcat的启动流程及执行流程都事无巨细得理清楚读明白了,下一篇文件我将总结从Tomcat的设计中我们应该学会什么以及我们应该怎么将这些经验使用到我们日常开发工作中。

文章转载自IT路人乙,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论