Tomcat请求处理(七) - Servlet实例的调用

本文详细解析了Tomcat中JSP请求的处理过程,包括Filter的调用机制、预编译模式以及如何通过查询字符串实现JSP预编译。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Tomcat请求处理中Servlet实例的调用是和Filter的调用联系在一起的,是在StandardWrapperValve类的#invoke()方法中调用的,前面的文章中提到过,就是下面的这句:

filterChain.doFilter(request.getRequest(), response.getResponse());

它的源代码如下:

Java代码 Tomcat请求处理(七) - Servlet实例的调用 - guoyang1982 - 游龙

  1. public void doFilter(ServletRequest request, ServletResponse response) throws IOException,   
  2.         ServletException {   
  3.   
  4.     if (Globals.IS_SECURITY_ENABLED) {   
  5.         final ServletRequest req = request;   
  6.         final ServletResponse res = response;   
  7.         try {   
  8.             java.security.AccessController   
  9.                     .doPrivileged(new java.security.PrivilegedExceptionAction() {   
  10.                         public Object run() throws ServletException, IOException {   
  11.                             internalDoFilter(req, res);   
  12.                             return null;   
  13.                         }   
  14.                     });   
  15.         } catch (PrivilegedActionException pe) {   
  16.             Exception e = pe.getException();   
  17.             if (e instanceof ServletException)   
  18.                 throw (ServletException) e;   
  19.             else if (e instanceof IOException)   
  20.                 throw (IOException) e;   
  21.             else if (e instanceof RuntimeException)   
  22.                 throw (RuntimeException) e;   
  23.             else  
  24.                 throw new ServletException(e.getMessage(), e);   
  25.         }   
  26.     } else {   
  27.         internalDoFilter(request, response);   
  28.     }   
  29. }  

Java代码 Tomcat请求处理(七) - Servlet实例的调用 - guoyang1982 - 游龙

  1. public void doFilter(ServletRequest request, ServletResponse response) throws IOException,   
  2.         ServletException {   
  3.   
  4.     if (Globals.IS_SECURITY_ENABLED) {   
  5.         final ServletRequest req = request;   
  6.         final ServletResponse res = response;   
  7.         try {   
  8.             java.security.AccessController   
  9.                     .doPrivileged(new java.security.PrivilegedExceptionAction() {   
  10.                         public Object run() throws ServletException, IOException {   
  11.                             internalDoFilter(req, res);   
  12.                             return null;   
  13.                         }   
  14.                     });   
  15.         } catch (PrivilegedActionException pe) {   
  16.             Exception e = pe.getException();   
  17.             if (e instanceof ServletException)   
  18.                 throw (ServletException) e;   
  19.             else if (e instanceof IOException)   
  20.                 throw (IOException) e;   
  21.             else if (e instanceof RuntimeException)   
  22.                 throw (RuntimeException) e;   
  23.             else  
  24.                 throw new ServletException(e.getMessage(), e);   
  25.         }   
  26.     } else {   
  27.         internalDoFilter(request, response);   
  28.     }   
  29. }  

这个方法只是调用了#internalDoFilter(),这个才是Filter调用的核心,源代码如下所示:

Java代码 Tomcat请求处理(七) - Servlet实例的调用 - guoyang1982 - 游龙

  1. private void internalDoFilter(ServletRequest request, ServletResponse response)   
  2.         throws IOException, ServletException {   
  3.   
  4.     if (pos < n) {   
  5.         ApplicationFilterConfig filterConfig = filters[pos++];   
  6.         Filter filter = null;   
  7.         try {   
  8.             // 得到当前Filter   
  9.             filter = filterConfig.getFilter();   
  10.             support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT, filter, request,   
  11.                     response);   
  12.             // 调用Filter的#doFilter()方法。   
  13.             // 在Filter内部,如果Filter调用成功,会调用chain.doFilter(request,response); 这个语句。   
  14.             // 这里传递给忒Filter的chain实例就是ApplicationFilterChain类的对象。   
  15.             // 于是程序又回到了#doFilter(),然后再次调用本方法。也应该是一种变相的递归了。   
  16.             if (Globals.IS_SECURITY_ENABLED) {   
  17.                 final ServletRequest req = request;   
  18.                 final ServletResponse res = response;   
  19.                 Principal principal = ((HttpServletRequest) req).getUserPrincipal();   
  20.   
  21.                 Object[] args = new Object[] { req, res, this };   
  22.                 SecurityUtil.doAsPrivilege("doFilter", filter, classType, args);   
  23.   
  24.                 args = null;   
  25.             } else {   
  26.                 filter.doFilter(request, response, this);   
  27.             }   
  28.   
  29.             support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request,   
  30.                     response);   
  31.         } catch (IOException e) {   
  32.             if (filter != null)   
  33.                 support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request,   
  34.                         response, e);   
  35.             throw e;   
  36.         } catch (ServletException e) {   
  37.             if (filter != null)   
  38.                 support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request,   
  39.                         response, e);   
  40.             throw e;   
  41.         } catch (RuntimeException e) {   
  42.             if (filter != null)   
  43.                 support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request,   
  44.                         response, e);   
  45.             throw e;   
  46.         } catch (Throwable e) {   
  47.             if (filter != null)   
  48.                 support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request,   
  49.                         response, e);   
  50.             throw new ServletException(sm.getString("filterChain.filter"), e);   
  51.         }   
  52.         return;   
  53.     }   
  54.   
  55.     try {   
  56.         if (Globals.STRICT_SERVLET_COMPLIANCE) {   
  57.             lastServicedRequest.set(request);   
  58.             lastServicedResponse.set(response);   
  59.         }   
  60.         // Filter全部调用完毕后,就会把请求真正的传递给Servlet了,调用它的#service()方法。   
  61.         support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT, servlet, request,   
  62.                 response);   
  63.         if ((request instanceof HttpServletRequest)   
  64.                 && (response instanceof HttpServletResponse)) {   
  65.   
  66.             if (Globals.IS_SECURITY_ENABLED) {   
  67.                 final ServletRequest req = request;   
  68.                 final ServletResponse res = response;   
  69.                 Principal principal = ((HttpServletRequest) req).getUserPrincipal();   
  70.                 Object[] args = new Object[] { req, res };   
  71.                 SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args,   
  72.                         principal);   
  73.                 args = null;   
  74.             } else {   
  75.                 servlet.service((HttpServletRequest) request, (HttpServletResponse) response);   
  76.             }   
  77.         } else {   
  78.             servlet.service(request, response);   
  79.         }   
  80.         support   
  81.                 .fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request,   
  82.                         response);   
  83.     } catch (IOException e) {   
  84.         support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request,   
  85.                 response, e);   
  86.         throw e;   
  87.     } catch (ServletException e) {   
  88.         support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request,   
  89.                 response, e);   
  90.         throw e;   
  91.     } catch (RuntimeException e) {   
  92.         support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request,   
  93.                 response, e);   
  94.         throw e;   
  95.     } catch (Throwable e) {   
  96.         support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request,   
  97.                 response, e);   
  98.         throw new ServletException(sm.getString("filterChain.servlet"), e);   
  99.     } finally {   
  100.         if (Globals.STRICT_SERVLET_COMPLIANCE) {   
  101.             lastServicedRequest.set(null);   
  102.             lastServicedResponse.set(null);   
  103.         }   
  104.     }   
  105.   
  106. }  

Java代码 Tomcat请求处理(七) - Servlet实例的调用 - guoyang1982 - 游龙

  1. private void internalDoFilter(ServletRequest request, ServletResponse response)   
  2.         throws IOException, ServletException {   
  3.   
  4.     if (pos < n) {   
  5.         ApplicationFilterConfig filterConfig = filters[pos++];   
  6.         Filter filter = null;   
  7.         try {   
  8.             // 得到当前Filter   
  9.             filter = filterConfig.getFilter();   
  10.             support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT, filter, request,   
  11.                     response);   
  12.             // 调用Filter的#doFilter()方法。   
  13.             // 在Filter内部,如果Filter调用成功,会调用chain.doFilter(request,response); 这个语句。   
  14.             // 这里传递给忒Filter的chain实例就是ApplicationFilterChain类的对象。   
  15.             // 于是程序又回到了#doFilter(),然后再次调用本方法。也应该是一种变相的递归了。   
  16.             if (Globals.IS_SECURITY_ENABLED) {   
  17.                 final ServletRequest req = request;   
  18.                 final ServletResponse res = response;   
  19.                 Principal principal = ((HttpServletRequest) req).getUserPrincipal();   
  20.   
  21.                 Object[] args = new Object[] { req, res, this };   
  22.                 SecurityUtil.doAsPrivilege("doFilter", filter, classType, args);   
  23.   
  24.                 args = null;   
  25.             } else {   
  26.                 filter.doFilter(request, response, this);   
  27.             }   
  28.   
  29.             support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request,   
  30.                     response);   
  31.         } catch (IOException e) {   
  32.             if (filter != null)   
  33.                 support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request,   
  34.                         response, e);   
  35.             throw e;   
  36.         } catch (ServletException e) {   
  37.             if (filter != null)   
  38.                 support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request,   
  39.                         response, e);   
  40.             throw e;   
  41.         } catch (RuntimeException e) {   
  42.             if (filter != null)   
  43.                 support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request,   
  44.                         response, e);   
  45.             throw e;   
  46.         } catch (Throwable e) {   
  47.             if (filter != null)   
  48.                 support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request,   
  49.                         response, e);   
  50.             throw new ServletException(sm.getString("filterChain.filter"), e);   
  51.         }   
  52.         return;   
  53.     }   
  54.   
  55.     try {   
  56.         if (Globals.STRICT_SERVLET_COMPLIANCE) {   
  57.             lastServicedRequest.set(request);   
  58.             lastServicedResponse.set(response);   
  59.         }   
  60.         // Filter全部调用完毕后,就会把请求真正的传递给Servlet了,调用它的#service()方法。   
  61.         support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT, servlet, request,   
  62.                 response);   
  63.         if ((request instanceof HttpServletRequest)   
  64.                 && (response instanceof HttpServletResponse)) {   
  65.   
  66.             if (Globals.IS_SECURITY_ENABLED) {   
  67.                 final ServletRequest req = request;   
  68.                 final ServletResponse res = response;   
  69.                 Principal principal = ((HttpServletRequest) req).getUserPrincipal();   
  70.                 Object[] args = new Object[] { req, res };   
  71.                 SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args,   
  72.                         principal);   
  73.                 args = null;   
  74.             } else {   
  75.                 servlet.service((HttpServletRequest) request, (HttpServletResponse) response);   
  76.             }   
  77.         } else {   
  78.             servlet.service(request, response);   
  79.         }   
  80.         support   
  81.                 .fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request,   
  82.                         response);   
  83.     } catch (IOException e) {   
  84.         support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request,   
  85.                 response, e);   
  86.         throw e;   
  87.     } catch (ServletException e) {   
  88.         support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request,   
  89.                 response, e);   
  90.         throw e;   
  91.     } catch (RuntimeException e) {   
  92.         support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request,   
  93.                 response, e);   
  94.         throw e;   
  95.     } catch (Throwable e) {   
  96.         support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request,   
  97.                 response, e);   
  98.         throw new ServletException(sm.getString("filterChain.servlet"), e);   
  99.     } finally {   
  100.         if (Globals.STRICT_SERVLET_COMPLIANCE) {   
  101.             lastServicedRequest.set(null);   
  102.             lastServicedResponse.set(null);   
  103.         }   
  104.     }   
  105.   
  106. }  

程序到了这里,就出现了分水岭,分别对应着对Servlet,JSP,和静态资源的处理。

Servlet比较简单,因为这里的"servlet"就是真实的Servlet实例了,直接调用开发人员自己编写的#service()方法了(#service()内部是会调用#doGet(),#doPost()等方法的)。

对于静态资源,是调用org.apache.catalina.servlets.DefaultServlet的#service()方法,由于DefaultServlet并没有重写这个方法,所以直接使用HttpServlet的#service()方法。但是DefaultServlet重写了#doGet(),#doPost()等方法(#doPost()内部又调用了#doGet()),所以请求就又到了#doGet()这个方法中,DefaultServlet的#doGet()只调用了#serveResource()这个方法来提取资源,代码太长,就不再仔细的看了。

对于JSP资源,就比较复杂了,还有编译等操作,下面来重点看一下。

首先,要调用org.apache.jasper.servlet.JspServlet的#service()方法,源代码如下:

Java代码 Tomcat请求处理(七) - Servlet实例的调用 - guoyang1982 - 游龙

  1. public void service(HttpServletRequest request, HttpServletResponse response)   
  2.         throws ServletException, IOException {   
  3.   
  4.     // 获得请求的JSP文件的路径   
  5.     String jspUri = null;   
  6.   
  7.     String jspFile = (String) request.getAttribute(Constants.JSP_FILE);   
  8.     if (jspFile != null) {   
  9.         // 通过web.xml中的<jsp-file>标签定义   
  10.         jspUri = jspFile;   
  11.     } else {   
  12.         jspUri = (String) request.getAttribute(Constants.INC_SERVLET_PATH);   
  13.         if (jspUri != null) {   
  14.             String pathInfo = (String) request.getAttribute("javax.servlet.include.path_info");   
  15.             if (pathInfo != null) {   
  16.                 jspUri += pathInfo;   
  17.             }   
  18.         } else {   
  19.             jspUri = request.getServletPath();   
  20.             String pathInfo = request.getPathInfo();   
  21.             if (pathInfo != null) {   
  22.                 jspUri += pathInfo;   
  23.             }   
  24.         }   
  25.     }   
  26.   
  27.     if (log.isDebugEnabled()) {   
  28.         log.debug("JspEngine --> " + jspUri);   
  29.         log.debug("\t     ServletPath: " + request.getServletPath());   
  30.         log.debug("\t        PathInfo: " + request.getPathInfo());   
  31.         log.debug("\t        RealPath: " + context.getRealPath(jspUri));   
  32.         log.debug("\t      RequestURI: " + request.getRequestURI());   
  33.         log.debug("\t     QueryString: " + request.getQueryString());   
  34.         log.debug("\t  Request Params: ");   
  35.         Enumeration e = request.getParameterNames();   
  36.         while (e.hasMoreElements()) {   
  37.             String name = (String) e.nextElement();   
  38.             log.debug("\t\t " + name + " = " + request.getParameter(name));   
  39.         }   
  40.     }   
  41.   
  42.     try {   
  43.         // 预编译模式,如果是预编译模式,只是对JSP进行编译,不会返回页面执行结果   
  44.         boolean precompile = preCompile(request);   
  45.         // 继续JSP请求   
  46.         serviceJspFile(request, response, jspUri, null, precompile);   
  47.     } catch (RuntimeException e) {   
  48.         throw e;   
  49.     } catch (ServletException e) {   
  50.         throw e;   
  51.     } catch (IOException e) {   
  52.         throw e;   
  53.     } catch (Throwable e) {   
  54.         throw new ServletException(e);   
  55.     }   
  56.   
  57. }  

这里,有两个方法需要看一下,一个是预编译方法#preCompile(),另外一个是调用JSP的#serviceJspFile()方法。

首先来看一下#preCompile():

Java代码 Tomcat请求处理(七) - Servlet实例的调用 - guoyang1982 - 游龙

  1. boolean preCompile(HttpServletRequest request) throws ServletException {   
  2.     // 获得查询字符串   
  3.     String queryString = request.getQueryString();   
  4.     if (queryString == null) {   
  5.         return (false);   
  6.     }   
  7.     // 看是否有预编译参数,默认是jsp_precompile   
  8.     int start = queryString.indexOf(Constants.PRECOMPILE);   
  9.     if (start < 0) {   
  10.         return (false);   
  11.     }   
  12.     queryString = queryString.substring(start + Constants.PRECOMPILE.length());   
  13.     if (queryString.length() == 0) {   
  14.         return (true);   
  15.     }   
  16.     if (queryString.startsWith("&")) {   
  17.         return (true);   
  18.     }   
  19.     if (!queryString.startsWith("=")) {   
  20.         return (false);   
  21.     }   
  22.     int limit = queryString.length();   
  23.     int ampersand = queryString.indexOf("&");   
  24.     if (ampersand > 0) {   
  25.         limit = ampersand;   
  26.     }   
  27.     String value = queryString.substring(1, limit);   
  28.     // 如果jsp_precompile的值为true,代表此次请求为预编译JSP的请求。   
  29.     if (value.equals("true")) {   
  30.         return (true);   
  31.     } else if (value.equals("false")) {   
  32.         // 这里要注意下,如果是false,同样是返回true的。   
  33.         // 因为规范中说如果这个参数是false,那么请求不应该被提交到JSP页面的,那么此次请求就变得毫无意义了,所以索性都预编译好了。   
  34.         return (true);   
  35.     } else {// 如果是true和false之外的值就要抛出异常了。   
  36.         throw new ServletException("Cannot have request parameter " + Constants.PRECOMPILE   
  37.                 + " set to " + value);   
  38.     }   
  39. }  

从这个方法中看出,要想预编译一个页面,只要在页面名字后加上查询字符串jsp_precompile=true就可以了。

下面是#serviceJspFile()方法,这个方法提供对请求进行了进一步的处理。

Java代码 Tomcat请求处理(七) - Servlet实例的调用 - guoyang1982 - 游龙

  1. private void serviceJspFile(HttpServletRequest request, HttpServletResponse response,   
  2.         String jspUri, Throwable exception, boolean precompile) throws ServletException,   
  3.         IOException {   
  4.     // 获取JspServletWrapper实例   
  5.     JspServletWrapper wrapper = (JspServletWrapper) rctxt.getWrapper(jspUri);   
  6.     if (wrapper == null) {   
  7.         synchronized (this) {   
  8.             wrapper = (JspServletWrapper) rctxt.getWrapper(jspUri);   
  9.             if (wrapper == null) {   
  10.                 if (null == context.getResource(jspUri)) {   
  11.                     String includeRequestUri = (String) request   
  12.                             .getAttribute("javax.servlet.include.request_uri");   
  13.                     if (includeRequestUri != null) {   
  14.                         String msg = Localizer.getMessage("jsp.error.file.not.found", jspUri);   
  15.                         throw new ServletException(SecurityUtil.filter(msg));   
  16.                     } else {   
  17.                         try {   
  18.                             response.sendError(HttpServletResponse.SC_NOT_FOUND, request   
  19.                                     .getRequestURI());   
  20.                         } catch (IllegalStateException ise) {   
  21.                             log.error(Localizer.getMessage("jsp.error.file.not.found", jspUri));   
  22.                         }   
  23.                     }   
  24.                     return;   
  25.                 }   
  26.                 boolean isErrorPage = exception != null;   
  27.                 wrapper = new JspServletWrapper(config, options, jspUri, isErrorPage, rctxt);   
  28.                 rctxt.addWrapper(jspUri, wrapper);   
  29.             }   
  30.         }   
  31.     }   
  32.        
  33.     // 调用它的#service()方法。   
  34.     wrapper.service(request, response, precompile);   
  35.   
  36. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值