- 浏览: 889910 次
- 性别:
- 来自: 武汉
文章分类
最新评论
-
小宇宙_WZY:
膜拜一下大神,解决了我一个大问题,非常感谢 orz
【解惑】深入jar包:从jar包中读取资源文件 -
JKL852qaz:
感谢,遇到相同的问题!
【解惑】深入jar包:从jar包中读取资源文件 -
lgh1992314:
为什么java中调用final方法是用invokevirtua ...
【解惑】Java动态绑定机制的内幕 -
鲁曼1991:
说的都有道理,protected只能被同一级包的类所调用
【解惑】真正理解了protected的作用范围 -
鲁曼1991:
...
【总结】String in Java
全文转载:http://luckaway.iteye.com/blog/557980
作者:luckaway [from javaeye]
网上已经有很多关于redirect和forward区别的文章,更多的都是只是一些概念上的描述,虽然在大多情况下,知道这些就已经足够了。但也有例外:forward not working for struts2,why?我也是在工作中碰到了这个问题,才特意看了下tomcat有关这部分的源代码。深刻的了解下也无妨。
redirect和forward都是属于servlet规范的,不同的servlet容器的实现可能会有一些区别,但原理都是类似的。
redirect和forward的定义:
1. redirect(重定向):服务端发送给客户端一个重定向的临时响应头,这个响应头包含重定向之后的URL,客户端用新的URL重新向服务器发送一个请求。
2. forward(请求转向):服务器程序内部请求转向,这个特性允许前一个程序用于处理请求,而后一个程序用来返回响应。
Redirect的原理比较简单,它的定义也已经描述的很清楚了,我也不想多讲什么,就贴一段简单的代码吧!
org.apache.catalina.connector.Response#sendRedirect(String):
void sendRedirect(String location) throws IOException { if (isCommitted()) throw new IllegalStateException (sm.getString("coyoteResponse.sendRedirect.ise")); // Ignore any call from an included servlet if (included) return; // Clear any data content that has been buffered resetBuffer(); // Generate a temporary redirect to the specified location try { String absolute = toAbsolute(location); setStatus(SC_FOUND); setHeader("Location", absolute); } catch (IllegalArgumentException e) { setStatus(SC_NOT_FOUND); } // Cause the response to be finished (from the application perspective) setSuspended(true); }
方法行为:先把相对路径转换成绝对路径,再包装一个包含有新的URL的临时响应头,“SC_FOUND”的值是302,就是重定向临时响应头的状态码。如果传入的“location”值不合法,就包装一个404的响应头。
下面就来看看tomcat是如何实现forward的,forward为什么在struts2下会无效(注解:其实是可以设置的)。
先看下程序是如何调用forward的:
req.getRequestDispatcher("testForward").forward(req, resp);
1. 得到一个请求调度器
2. 通过调度器把请求转发过去。
第一步骤,获取请求调度器。
org.apache.catalina.connector.Request#getRequestDispatcher(String)
public RequestDispatcher getRequestDispatcher(String path) { if (request == null) { throw new IllegalStateException( sm.getString("requestFacade.nullRequest")); } if (Globals.IS_SECURITY_ENABLED){ return (RequestDispatcher)AccessController.doPrivileged( new GetRequestDispatcherPrivilegedAction(path)); } else { return request.getRequestDispatcher(path); }
org.apache.catalina.connector.RequestFacade和类org.apache.catalina.connector.Request都是实现了javax.servlet.http.HttpServletRequest接口,而RequestFacade内部有包装了个Request,对Request的访问做了些控制,应该是代理模式
org.apache.catalina.connector.Request#getRequestDispatcher(String)
public RequestDispatcher getRequestDispatcher(String path) { if (path.startsWith("/")) return (context.getServletContext().getRequestDispatcher(path)); //省略了部分代码 return (context.getServletContext().getRequestDispatcher(relative)); }
接下来就转交给ServletContext来处理,ServletContext是web项目的一个上下文,包含所有的Servlet集合,还定义了一些Servlet与容器之间交互的接口。
org.apache.catalina.core.ApplicationContext#getRequestDispatcher(String)
public RequestDispatcher getRequestDispatcher(String path) { //省去部分代码 context.getMapper().map(uriMB, mappingData); //省去部分代码 Wrapper wrapper = (Wrapper) mappingData.wrapper; String wrapperPath = mappingData.wrapperPath.toString(); String pathInfo = mappingData.pathInfo.toString(); mappingData.recycle(); // Construct a RequestDispatcher to process this request return new ApplicationDispatcher (wrapper, uriCC.toString(), wrapperPath, pathInfo, queryString, null); }
该方法里非常关键的一行:context.getMapper().map(uriMB, mappingData)。
Mapper的类定义我不知道如何描述,就贴上原文吧:Mapper, which implements the servlet API mapping rules (which are derived from the HTTP rules)。
不过只想了解forward的原理,熟悉map函数就够了。
org.apache.tomcat.util.http.mapper.Mapper#map(org.apache.tomcat.util.buf.MessageBytes, org.apache.tomcat.util.http.mapper.MappingData):
public void map(MessageBytes uri, MappingData mappingData) throws Exception { uri.toChars(); CharChunk uricc = uri.getCharChunk(); uricc.setLimit(-1); internalMapWrapper(context, uricc, mappingData); }
org.apache.tomcat.util.http.mapper.Mapper#internalMapWrapper(Mapper$Context,org.apache.tomcat.util.buf.CharChunk, org.apache.tomcat.util.http.mapper.MappingData):
private final void internalMapWrapper(Context context, CharChunk path, MappingData mappingData) throws Exception { int pathOffset = path.getOffset(); int pathEnd = path.getEnd(); int servletPath = pathOffset; boolean noServletPath = false; int length = context.name.length(); if (length != (pathEnd - pathOffset)) { servletPath = pathOffset + length; } else { noServletPath = true; path.append('/'); pathOffset = path.getOffset(); pathEnd = path.getEnd(); servletPath = pathOffset+length; } path.setOffset(servletPath); // Rule 1 -- Exact Match Wrapper[] exactWrappers = context.exactWrappers; internalMapExactWrapper(exactWrappers, path, mappingData); // Rule 2 -- Prefix Match boolean checkJspWelcomeFiles = false; Wrapper[] wildcardWrappers = context.wildcardWrappers; if (mappingData.wrapper == null) { internalMapWildcardWrapper(wildcardWrappers, context.nesting, path, mappingData); if (mappingData.wrapper != null && mappingData.jspWildCard) { char[] buf = path.getBuffer(); if (buf[pathEnd - 1] == '/') { /* * Path ending in '/' was mapped to JSP servlet based on * wildcard match (e.g., as specified in url-pattern of a * jsp-property-group. * Force the context's welcome files, which are interpreted * as JSP files (since they match the url-pattern), to be * considered. See Bugzilla 27664. */ mappingData.wrapper = null; checkJspWelcomeFiles = true; } else { // See Bugzilla 27704 mappingData.wrapperPath.setChars(buf, path.getStart(), path.getLength()); mappingData.pathInfo.recycle(); } } } if(mappingData.wrapper == null && noServletPath) { // The path is empty, redirect to "/" mappingData.redirectPath.setChars (path.getBuffer(), pathOffset, pathEnd); path.setEnd(pathEnd - 1); return; } // Rule 3 -- Extension Match Wrapper[] extensionWrappers = context.extensionWrappers; if (mappingData.wrapper == null && !checkJspWelcomeFiles) { internalMapExtensionWrapper(extensionWrappers, path, mappingData); } // Rule 4 -- Welcome resources processing for servlets if (mappingData.wrapper == null) { boolean checkWelcomeFiles = checkJspWelcomeFiles; if (!checkWelcomeFiles) { char[] buf = path.getBuffer(); checkWelcomeFiles = (buf[pathEnd - 1] == '/'); } if (checkWelcomeFiles) { for (int i = 0; (i < context.welcomeResources.length) && (mappingData.wrapper == null); i++) { path.setOffset(pathOffset); path.setEnd(pathEnd); path.append(context.welcomeResources[i], 0, context.welcomeResources[i].length()); path.setOffset(servletPath); // Rule 4a -- Welcome resources processing for exact macth internalMapExactWrapper(exactWrappers, path, mappingData); // Rule 4b -- Welcome resources processing for prefix match if (mappingData.wrapper == null) { internalMapWildcardWrapper (wildcardWrappers, context.nesting, path, mappingData); } // Rule 4c -- Welcome resources processing // for physical folder if (mappingData.wrapper == null && context.resources != null) { Object file = null; String pathStr = path.toString(); try { file = context.resources.lookup(pathStr); } catch(NamingException nex) { // Swallow not found, since this is normal } if (file != null && !(file instanceof DirContext) ) { internalMapExtensionWrapper(extensionWrappers, path, mappingData); if (mappingData.wrapper == null && context.defaultWrapper != null) { mappingData.wrapper = context.defaultWrapper.object; mappingData.requestPath.setChars (path.getBuffer(), path.getStart(), path.getLength()); mappingData.wrapperPath.setChars (path.getBuffer(), path.getStart(), path.getLength()); mappingData.requestPath.setString(pathStr); mappingData.wrapperPath.setString(pathStr); } } } } path.setOffset(servletPath); path.setEnd(pathEnd); } }
可以看到这里有7个匹配Servlet规则:
1. Rule 1 -- Exact Match:精确匹配,匹配web.xml配置的格式如“<url-pattern>/testQiu</url-pattern>”的Servlet
2. Rule 2 -- Prefix Matcha:前缀匹配,匹配的Servlet格式如“<url-pattern>/testQiu/*</url-pattern>”
3. Rule 3 -- Extension Match:扩展匹配,匹配jsp或者jspx
4. ---Rule 4a -- Welcome resources processing for exact macth:
5. ---Rule 4b -- Welcome resources processing for prefix match:
6. ---Rule 4c -- Welcome resources processing for physical folder:
7. Rule 7 --如果前面6条都没匹配到,那就返回org.apache.catalina.servlets.DefaultServlet。
其实这里真正的匹配的是Wapper,而不是Servlet,因为Wapper最重要的一个属性就是Servlet,说成“匹配Servlet”是为了更容易的表达。
至此返回RequestDispatcher就结束了。
接下来就是讲解RequestDispatcher.forward了。Forward的就不贴出全部的源代码,只贴一些重要的片段,绝大部分的逻辑都在org.apache.catalina.core.ApplicationDispatcher类里。
先描述下过程:
1. 设置request里的部分属性值,如:请求的路径、参数等。
2. 组装一个FilterChain链,调用doFilter方法。
3. 最后根据实际情况调用Filter的doFilter函数或者Servlet的service函数。
注:FilterChain和Filter是两个不同的接口,两个接口的UML
org.apache.catalina.core.ApplicationDispatcher#doForward(ServletRequest,ServletResponse):
private void doForward(ServletRequest request, ServletResponse response) throws ServletException, IOException //省略了部分代码 // Handle an HTTP named dispatcher forward if ((servletPath == null) && (pathInfo == null)) { //省略了部分代码 } else {// Handle an HTTP path-based forward ApplicationHttpRequest wrequest = (ApplicationHttpRequest) wrapRequest(state); String contextPath = context.getPath(); HttpServletRequest hrequest = state.hrequest; if (hrequest.getAttribute(Globals.FORWARD_REQUEST_URI_ATTR) == null) { wrequest.setAttribute(Globals.FORWARD_REQUEST_URI_ATTR, hrequest.getRequestURI()); wrequest.setAttribute(Globals.FORWARD_CONTEXT_PATH_ATTR, hrequest.getContextPath()); wrequest.setAttribute(Globals.FORWARD_SERVLET_PATH_ATTR, hrequest.getServletPath()); wrequest.setAttribute(Globals.FORWARD_PATH_INFO_ATTR, hrequest.getPathInfo()); wrequest.setAttribute(Globals.FORWARD_QUERY_STRING_ATTR, hrequest.getQueryString()); } wrequest.setContextPath(contextPath); wrequest.setRequestURI(requestURI); wrequest.setServletPath(servletPath); wrequest.setPathInfo(pathInfo); if (queryString != null) { wrequest.setQueryString(queryString); wrequest.setQueryParams(queryString); } processRequest(request,response,state); } } //省略了部分代码 }
wrequest.setContextPath(contextPath); wrequest.setRequestURI(requestURI); wrequest.setServletPath(servletPath); wrequest.setPathInfo(pathInfo); if (queryString != null) { wrequest.setQueryString(queryString); wrequest.setQueryParams(queryString); }
第2步:组装FitlerChain链,根据web.xml配置信息,是否决定添加Filter----
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
org.apache.catalina.core.ApplicationFilterFactory#createFilterChain(ServletRequest, Wrapper, Servlet):
public ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) { //省略部分代码 filterChain = new ApplicationFilterChain(); } filterChain.setServlet(servlet); filterChain.setSupport (((StandardWrapper)wrapper).getInstanceSupport()); // Acquire the filter mappings for this Context StandardContext context = (StandardContext) wrapper.getParent(); FilterMap filterMaps[] = context.findFilterMaps(); // If there are no filter mappings, we are done if ((filterMaps == null) || (filterMaps.length == 0)) return (filterChain); // Acquire the information we will need to match filter mappings String servletName = wrapper.getName(); // Add the relevant path-mapped filters to this filter chain for (int i = 0; i < filterMaps.length; i++) { if (!matchDispatcher(filterMaps[i] ,dispatcher)) { continue; } if (!matchFiltersURL(filterMaps[i], requestPath)) continue; ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName()); if (filterConfig == null) { ; // FIXME - log configuration problem continue; } boolean isCometFilter = false; if (comet) { try { isCometFilter = filterConfig.getFilter() instanceof CometFilter; } catch (Exception e) { // Note: The try catch is there because getFilter has a lot of // declared exceptions. However, the filter is allocated much // earlier } if (isCometFilter) { filterChain.addFilter(filterConfig); } } else { filterChain.addFilter(filterConfig); } } //省略部分代码 // Return the completed filter chain return (filterChain); }
如果是<dispatcher>REQUEST</dispatcher>,那就不添加Filter,默认设置是REQUEST
如果是<dispatcher>FORWARD</dispatcher>,添加Filter到FilterChain。
第3步:调用doFilter或者service,代码删减了很多。
org.apache.catalina.core.ApplicationFilterChain#doFilter(ServletRequest, ServletResponse):
public void doFilter(ServletRequest request, ServletResponse response)throws IOException, ServletException { internalDoFilter(request,response); } org.apache.catalina.core.ApplicationFilterChain#internalDoFilter(ServletRequest, ServletResponse) private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { // Call the next filter if there is one if (pos < n) { filter.doFilter(request, response, this); return; } servlet.service((HttpServletRequest) request,(HttpServletResponse) response); }
<filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping>
相关推荐
Tomcat源码Tomcat源码Tomcat源码Tomcat源码Tomcat源码Tomcat源码Tomcat源码
tomcat的源码解析资料,加上tomcat的pdf解析中文版的,tomcat的源码。 挺不错的资源,大家可以学习下。
how tomcat work源码及中文翻译
MyEclipse导入Tomcat源码.wps文档说明 apache-tomcat-6.0.37-src.zip源码
3-6Tomcat处理源码实现与异步Servlet源码实现(1).mp4
tomcat7源码下载tomcat7源码下载tomcat7源码下载tomcat7源码下载tomcat7源码下载tomcat7源码下载tomcat7源码下载tomcat7源码下载
文件描述: 1.tomcat源码文件 2.tomcat程序文件 3.Ant程序文件 4.tomcat源码在eclipse运行,重新编译,重新打包步骤文件 ...1.用于学习tomcat源码和了解tomcat运行机制 2.学习如何修改tomcat源码后如何重新编译,打包。
Tomcat源码剖析 : 整体架构 层层分析 源码解析 架构分析 (Http服务器功能:Socket通信(TCP/IP)、解析Http报文 Servlet容器功能:有很多Servlet(自带系统级Servlet+自定义Servlet),Servlet处理具体的业务逻辑...
手把手教你搭建Tomcat7源码开发环境,如果有了如下说明还搭建不成功,建议考虑下自己的理解能力,能不能走编码这条路。附近包括Tomcat源码、Tomcat源码开发环境依赖的包、Tomcat的环境编译工具ant 1、建立如下...
tomcat 源码分析系列文档 http深入分析,tomcat中类的加载,tomcat的启动,tomcat的容器思想,
tomcat的源码包
tomcat源码导入myeclipse,里面包括所支持的jar文件与详细的导入说明
tomcat 源码tomcat 源码tomcat 源码tomcat 源码tomcat 源码tomcat 源码tomcat 源码tomcat 源码tomcat 源码tomcat 源码tomcat 源码tomcat 源码tomcat 源码tomcat 源码tomcat 源码
tomcat源码解析
tomcat的简易源码实现,参考详细内容请搜索csdn用户yang134679
Tomcat源码研究.pdf
java源码和tomcat源码
tomcat7的源码项目,可以导入到eclipse,自己编译运行tomcat。
Tomcat 源码研究 全面解析Tomcat源码