Tomcat请求处理(七) - Servlet实例的调用
来源:互联网 发布:好看的网络自制剧古装 编辑:程序博客网 时间:2024/06/05 07:07
Tomcat请求处理中Servlet实例的调用是和Filter的调用联系在一起的,是在StandardWrapperValve类的#invoke()方法中调用的,前面的文章中提到过,就是下面的这句:
filterChain.doFilter(request.getRequest(), response.getResponse());
它的源代码如下:
这个方法只是调用了#internalDoFilter(),这个才是Filter调用的核心,源代码如下所示:
程序到了这里,就出现了分水岭,分别对应着对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()方法,源代码如下:
这里,有两个方法需要看一下,一个是预编译方法#preCompile(),另外一个是调用JSP的#serviceJspFile()方法。
首先来看一下#preCompile():
从这个方法中看出,要想预编译一个页面,只要在页面名字后加上查询字符串jsp_precompile=true就可以了。
下面是#serviceJspFile()方法,这个方法提供对请求进行了进一步的处理。
org.apache.jasper.servlet.JspServletWrapper的service()方法包括了编译,载入和执行Servlet几个步骤,如下所示:
#compile()方法实现了对JSP文件向java类(Servlet)的编译,源代码如下所示:
可以看出,JSP页面没有修改的前提下Tomcat是不会对JSP进行多次编译的,只在第一次调用它的时候编译。
看完#compile()后,再来看一下#getServlet(),它载入了前面编译生成的Servlet类。
通过这个类,获得了一个"theServlet"实例作为JSP编译之后的Servlet引用,并且在JSP没有改动前,这个实例是不需要重新生成的。
通过这两个步骤后,最后调用了JSP编译后的Servlet类的#service()方法去处理请求。至此,Tomcat的请求处理结束了。
filterChain.doFilter(request.getRequest(), response.getResponse());
它的源代码如下:
- public void doFilter(ServletRequest request, ServletResponse response) throws IOException,
- ServletException {
- if (Globals.IS_SECURITY_ENABLED) {
- final ServletRequest req = request;
- final ServletResponse res = response;
- try {
- java.security.AccessController
- .doPrivileged(new java.security.PrivilegedExceptionAction() {
- public Object run() throws ServletException, IOException {
- internalDoFilter(req, res);
- return null;
- }
- });
- } catch (PrivilegedActionException pe) {
- Exception e = pe.getException();
- if (e instanceof ServletException)
- throw (ServletException) e;
- else if (e instanceof IOException)
- throw (IOException) e;
- else if (e instanceof RuntimeException)
- throw (RuntimeException) e;
- else
- throw new ServletException(e.getMessage(), e);
- }
- } else {
- internalDoFilter(request, response);
- }
- }
这个方法只是调用了#internalDoFilter(),这个才是Filter调用的核心,源代码如下所示:
- private void internalDoFilter(ServletRequest request, ServletResponse response)
- throws IOException, ServletException {
- if (pos < n) {
- ApplicationFilterConfig filterConfig = filters[pos++];
- Filter filter = null;
- try {
- // 得到当前Filter
- filter = filterConfig.getFilter();
- support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT, filter, request,
- response);
- // 调用Filter的#doFilter()方法。
- // 在Filter内部,如果Filter调用成功,会调用chain.doFilter(request,response); 这个语句。
- // 这里传递给忒Filter的chain实例就是ApplicationFilterChain类的对象。
- // 于是程序又回到了#doFilter(),然后再次调用本方法。也应该是一种变相的递归了。
- if (Globals.IS_SECURITY_ENABLED) {
- final ServletRequest req = request;
- final ServletResponse res = response;
- Principal principal = ((HttpServletRequest) req).getUserPrincipal();
- Object[] args = new Object[] { req, res, this };
- SecurityUtil.doAsPrivilege("doFilter", filter, classType, args);
- args = null;
- } else {
- filter.doFilter(request, response, this);
- }
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request,
- response);
- } catch (IOException e) {
- if (filter != null)
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request,
- response, e);
- throw e;
- } catch (ServletException e) {
- if (filter != null)
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request,
- response, e);
- throw e;
- } catch (RuntimeException e) {
- if (filter != null)
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request,
- response, e);
- throw e;
- } catch (Throwable e) {
- if (filter != null)
- support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request,
- response, e);
- throw new ServletException(sm.getString("filterChain.filter"), e);
- }
- return;
- }
- try {
- if (Globals.STRICT_SERVLET_COMPLIANCE) {
- lastServicedRequest.set(request);
- lastServicedResponse.set(response);
- }
- // Filter全部调用完毕后,就会把请求真正的传递给Servlet了,调用它的#service()方法。
- support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT, servlet, request,
- response);
- if ((request instanceof HttpServletRequest)
- && (response instanceof HttpServletResponse)) {
- if (Globals.IS_SECURITY_ENABLED) {
- final ServletRequest req = request;
- final ServletResponse res = response;
- Principal principal = ((HttpServletRequest) req).getUserPrincipal();
- Object[] args = new Object[] { req, res };
- SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args,
- principal);
- args = null;
- } else {
- servlet.service((HttpServletRequest) request, (HttpServletResponse) response);
- }
- } else {
- servlet.service(request, response);
- }
- support
- .fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request,
- response);
- } catch (IOException e) {
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request,
- response, e);
- throw e;
- } catch (ServletException e) {
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request,
- response, e);
- throw e;
- } catch (RuntimeException e) {
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request,
- response, e);
- throw e;
- } catch (Throwable e) {
- support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request,
- response, e);
- throw new ServletException(sm.getString("filterChain.servlet"), e);
- } finally {
- if (Globals.STRICT_SERVLET_COMPLIANCE) {
- lastServicedRequest.set(null);
- lastServicedResponse.set(null);
- }
- }
- }
程序到了这里,就出现了分水岭,分别对应着对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()方法,源代码如下:
- public void service(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- // 获得请求的JSP文件的路径
- String jspUri = null;
- String jspFile = (String) request.getAttribute(Constants.JSP_FILE);
- if (jspFile != null) {
- // 通过web.xml中的<jsp-file>标签定义
- jspUri = jspFile;
- } else {
- jspUri = (String) request.getAttribute(Constants.INC_SERVLET_PATH);
- if (jspUri != null) {
- String pathInfo = (String) request.getAttribute("javax.servlet.include.path_info");
- if (pathInfo != null) {
- jspUri += pathInfo;
- }
- } else {
- jspUri = request.getServletPath();
- String pathInfo = request.getPathInfo();
- if (pathInfo != null) {
- jspUri += pathInfo;
- }
- }
- }
- if (log.isDebugEnabled()) {
- log.debug("JspEngine --> " + jspUri);
- log.debug("\t ServletPath: " + request.getServletPath());
- log.debug("\t PathInfo: " + request.getPathInfo());
- log.debug("\t RealPath: " + context.getRealPath(jspUri));
- log.debug("\t RequestURI: " + request.getRequestURI());
- log.debug("\t QueryString: " + request.getQueryString());
- log.debug("\t Request Params: ");
- Enumeration e = request.getParameterNames();
- while (e.hasMoreElements()) {
- String name = (String) e.nextElement();
- log.debug("\t\t " + name + " = " + request.getParameter(name));
- }
- }
- try {
- // 预编译模式,如果是预编译模式,只是对JSP进行编译,不会返回页面执行结果
- boolean precompile = preCompile(request);
- // 继续JSP请求
- serviceJspFile(request, response, jspUri, null, precompile);
- } catch (RuntimeException e) {
- throw e;
- } catch (ServletException e) {
- throw e;
- } catch (IOException e) {
- throw e;
- } catch (Throwable e) {
- throw new ServletException(e);
- }
- }
这里,有两个方法需要看一下,一个是预编译方法#preCompile(),另外一个是调用JSP的#serviceJspFile()方法。
首先来看一下#preCompile():
- boolean preCompile(HttpServletRequest request) throws ServletException {
- // 获得查询字符串
- String queryString = request.getQueryString();
- if (queryString == null) {
- return (false);
- }
- // 看是否有预编译参数,默认是jsp_precompile
- int start = queryString.indexOf(Constants.PRECOMPILE);
- if (start < 0) {
- return (false);
- }
- queryString = queryString.substring(start + Constants.PRECOMPILE.length());
- if (queryString.length() == 0) {
- return (true);
- }
- if (queryString.startsWith("&")) {
- return (true);
- }
- if (!queryString.startsWith("=")) {
- return (false);
- }
- int limit = queryString.length();
- int ampersand = queryString.indexOf("&");
- if (ampersand > 0) {
- limit = ampersand;
- }
- String value = queryString.substring(1, limit);
- // 如果jsp_precompile的值为true,代表此次请求为预编译JSP的请求。
- if (value.equals("true")) {
- return (true);
- } else if (value.equals("false")) {
- // 这里要注意下,如果是false,同样是返回true的。
- // 因为规范中说如果这个参数是false,那么请求不应该被提交到JSP页面的,那么此次请求就变得毫无意义了,所以索性都预编译好了。
- return (true);
- } else {// 如果是true和false之外的值就要抛出异常了。
- throw new ServletException("Cannot have request parameter " + Constants.PRECOMPILE
- + " set to " + value);
- }
- }
从这个方法中看出,要想预编译一个页面,只要在页面名字后加上查询字符串jsp_precompile=true就可以了。
下面是#serviceJspFile()方法,这个方法提供对请求进行了进一步的处理。
- private void serviceJspFile(HttpServletRequest request, HttpServletResponse response,
- String jspUri, Throwable exception, boolean precompile) throws ServletException,
- IOException {
- // 获取JspServletWrapper实例
- JspServletWrapper wrapper = (JspServletWrapper) rctxt.getWrapper(jspUri);
- if (wrapper == null) {
- synchronized (this) {
- wrapper = (JspServletWrapper) rctxt.getWrapper(jspUri);
- if (wrapper == null) {
- if (null == context.getResource(jspUri)) {
- String includeRequestUri = (String) request
- .getAttribute("javax.servlet.include.request_uri");
- if (includeRequestUri != null) {
- String msg = Localizer.getMessage("jsp.error.file.not.found", jspUri);
- throw new ServletException(SecurityUtil.filter(msg));
- } else {
- try {
- response.sendError(HttpServletResponse.SC_NOT_FOUND, request
- .getRequestURI());
- } catch (IllegalStateException ise) {
- log.error(Localizer.getMessage("jsp.error.file.not.found", jspUri));
- }
- }
- return;
- }
- boolean isErrorPage = exception != null;
- wrapper = new JspServletWrapper(config, options, jspUri, isErrorPage, rctxt);
- rctxt.addWrapper(jspUri, wrapper);
- }
- }
- }
- // 调用它的#service()方法。
- wrapper.service(request, response, precompile);
- }
org.apache.jasper.servlet.JspServletWrapper的service()方法包括了编译,载入和执行Servlet几个步骤,如下所示:
- public void service(HttpServletRequest request, HttpServletResponse response, boolean precompile)
- throws ServletException, IOException, FileNotFoundException {
- try {
- if (ctxt.isRemoved()) {
- throw new FileNotFoundException(jspUri);
- }
- if ((available > 0L) && (available < Long.MAX_VALUE)) {
- if (available > System.currentTimeMillis()) {
- response.setDateHeader("Retry-After", available);
- response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, Localizer
- .getMessage("jsp.error.unavailable"));
- return;
- } else {
- // Wait period has expired. Reset.
- available = 0;
- }
- }
- // (1) 编译
- if (options.getDevelopment() || firstTime) {
- synchronized (this) {
- firstTime = false;
- // The following sets reload to true, if necessary
- ctxt.compile();
- }
- } else {
- if (compileException != null) {
- // Throw cached compilation exception
- throw compileException;
- }
- }
- // (2) 载入Servlet类文件
- getServlet();
- // 如果是预编译,那么直接返回
- if (precompile) {
- return;
- }
- } catch (ServletException ex) {
- if (options.getDevelopment()) {
- throw handleJspException(ex);
- } else {
- throw ex;
- }
- } catch (IOException ex) {
- if (options.getDevelopment()) {
- throw handleJspException(ex);
- } else {
- throw ex;
- }
- } catch (IllegalStateException ex) {
- if (options.getDevelopment()) {
- throw handleJspException(ex);
- } else {
- throw ex;
- }
- } catch (Exception ex) {
- if (options.getDevelopment()) {
- throw handleJspException(ex);
- } else {
- throw new JasperException(ex);
- }
- }
- try {
- // (3) 调用Servlet处理请求
- if (theServlet instanceof SingleThreadModel) {
- synchronized (this) {
- theServlet.service(request, response);
- }
- } else {
- theServlet.service(request, response);
- }
- } catch (UnavailableException ex) {
- String includeRequestUri = (String) request
- .getAttribute("javax.servlet.include.request_uri");
- if (includeRequestUri != null) {
- throw ex;
- } else {
- int unavailableSeconds = ex.getUnavailableSeconds();
- if (unavailableSeconds <= 0) {
- unavailableSeconds = 60; // Arbitrary default
- }
- available = System.currentTimeMillis() + (unavailableSeconds * 1000L);
- response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, ex.getMessage());
- }
- } catch (ServletException ex) {
- if (options.getDevelopment()) {
- throw handleJspException(ex);
- } else {
- throw ex;
- }
- } catch (IOException ex) {
- if (options.getDevelopment()) {
- throw handleJspException(ex);
- } else {
- throw ex;
- }
- } catch (IllegalStateException ex) {
- if (options.getDevelopment()) {
- throw handleJspException(ex);
- } else {
- throw ex;
- }
- } catch (Exception ex) {
- if (options.getDevelopment()) {
- throw handleJspException(ex);
- } else {
- throw new JasperException(ex);
- }
- }
- }
#compile()方法实现了对JSP文件向java类(Servlet)的编译,源代码如下所示:
- public void compile() throws JasperException, FileNotFoundException {
- // 创建"jspCompiler"的实例
- createCompiler();
- // 判断是否过期,即是否需要重新编译,这个方法中主要用源代码和编译后的文件的修改时间等因素进行判断
- if (jspCompiler.isOutDated()) {
- try {
- jspCompiler.removeGeneratedFiles();
- jspLoader = null;
- // 执行编译
- jspCompiler.compile();
- // 设定reload标志为true,让#getServlet()方法载入新的类
- jsw.setReload(true);
- jsw.setCompilationException(null);
- } catch (JasperException ex) {
- // Cache compilation exception
- jsw.setCompilationException(ex);
- throw ex;
- } catch (Exception ex) {
- JasperException je = new JasperException(
- Localizer.getMessage("jsp.error.unable.compile"),
- ex);
- // Cache compilation exception
- jsw.setCompilationException(je);
- throw je;
- }
- }
- }
可以看出,JSP页面没有修改的前提下Tomcat是不会对JSP进行多次编译的,只在第一次调用它的时候编译。
看完#compile()后,再来看一下#getServlet(),它载入了前面编译生成的Servlet类。
- public Servlet getServlet() throws ServletException, IOException, FileNotFoundException {
- if (reload) {
- synchronized (this) {
- if (reload) {
- // 销毁旧的Servlet
- destroy();
- Servlet servlet = null;
- try {
- // 载入Servlet
- servletClass = ctxt.load();
- servlet = (Servlet) servletClass.newInstance();
- AnnotationProcessor annotationProcessor = (AnnotationProcessor) config
- .getServletContext().getAttribute(
- AnnotationProcessor.class.getName());
- if (annotationProcessor != null) {
- annotationProcessor.processAnnotations(servlet);
- annotationProcessor.postConstruct(servlet);
- }
- } catch (IllegalAccessException e) {
- throw new JasperException(e);
- } catch (InstantiationException e) {
- throw new JasperException(e);
- } catch (Exception e) {
- throw new JasperException(e);
- }
- // Servlet初始化
- servlet.init(config);
- if (!firstTime) {
- ctxt.getRuntimeContext().incrementJspReloadCount();
- }
- theServlet = servlet;
- // reload值复原
- reload = false;
- }
- }
- }
- return theServlet;
- }
通过这个类,获得了一个"theServlet"实例作为JSP编译之后的Servlet引用,并且在JSP没有改动前,这个实例是不需要重新生成的。
通过这两个步骤后,最后调用了JSP编译后的Servlet类的#service()方法去处理请求。至此,Tomcat的请求处理结束了。
0 0
- Tomcat请求处理(七) - Servlet实例的调用
- Tomcat请求处理(六) -- Servlet实例创建
- Tomcat一次http请求处理,filter、servlet调用流程
- servlet图解。1。。tomcat处理请求过程、servlet的生命周期
- tomcat请求处理分析(六)servlet的处理过程
- Servlet之tomcat处理Http请求的流程
- Servlet请求的处理
- Servlet容器 - Tomcat请求处理原理
- tomcat上servlet程序的配置与处理servlet请求过程
- 并发实例,10000个http请求同时访问tomcat,tomcat是怎么处理的?
- tomcat处理请求的过程
- tomcat处理请求的过程
- 异步处理Servlet接收到的请求
- Tomcat处理HTTP请求源码分析--包含jsp内置对象的实例过程
- Tomcat处理HTTP请求源码分析--包含jsp内置对象的实例过程
- 打开Tomcat的servlet调用器功能
- web请求:servlet处理请求
- tomcat请求处理分析(一) 启动container实例
- private static final long serialVersionUID = 1L 的作用
- MySQL Connections to sleep
- 浅谈前端
- 配置WDK7600+VS2010
- http Content-Type 中的boundary捣乱,导致服务端无法获取上传文件
- Tomcat请求处理(七) - Servlet实例的调用
- Java Servlet 获取服务器各种路径信息
- 屏蔽“您目前使用的Discuz!程序有新版本发布,请及时升级!”提示
- HDOJ-2025解题报告
- iOS-CoreData
- Tomcat请求处理(六) -- Servlet实例创建
- vc 用函数读写INI配置文件
- Eclipse中用Git导入Maven项目
- linux service命令