`
luckaway
  • 浏览: 137015 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

tomcat源码—redirect和forward的实现

阅读更多
网上已经有很多关于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):
  
 public 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);  
        }

方法行为:把获取RequestDispatcher的任务交个内部的request。它们之间的关系如下所示




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)); 

    }

方法行为:把绝对路径转换成相对路径,最终的格式如“/testForward”。若已经是这种格式的相对路径,就无需再转换了。
接下来就转交给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); 
    }

方法行为:根据路径名“path”找到一个包含有Servlet的Wrapper,最后实例化一个ApplicationDispatcher,并且返回该ApplicationDispatcher。

该方法里非常关键的一行: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);

    }

方法行为:。。。。。。。就介绍下参数吧,uri可以理解是path(“/testforward”)的一个变形,而mappingData用于存储当前线程用到的部分数据。该函数是没有返回值的,处理之后的结果就是存放到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);
            }
                                        
        }

方法行为:通过“path”从“context”里找到对应的Servlet,存放到“mappingData”里。
可以看到这里有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);
        }
        }
//省略了部分代码
    }

第1步:设置新的request的属性:
         
  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非常了解的,根本就不需要花那么多时间去查看tomcat源代码。只要在web.xml增加一点配置就OK了。
<filter-mapping>
		<filter-name>struts2</filter-name>
		<url-pattern>/*</url-pattern>
		<dispatcher>REQUEST</dispatcher>
		<dispatcher>FORWARD</dispatcher>
</filter-mapping>





  • 大小: 25.9 KB
  • 大小: 33.1 KB
分享到:
评论
32 楼 cutesource 2010-07-20  
<div class="quote_title">lsb 写道</div>
<div class="quote_div">    LZ 看这个有什么好的资料吗?推荐下看看。</div>
<p> </p>
<p>推荐你看<a href="http://download.csdn.net/source/1039743">《how tomcat works》</a></p>
<p>另外我也做过有关tomcat的源码分析,有兴趣可以参考一下:</p>
<h1 class="title_txt"><span style="font-size: medium;"><a href="http://blog.csdn.net/cutesource/archive/2009/12/14/5006062.aspx">Tomcat
源码分析(一)------ 架构</a></span></h1>
<h1 class="title_txt"><span style="font-size: medium;"><a href="http://blog.csdn.net/cutesource/archive/2009/12/19/5040417.aspx">Tomcat
源码分析(二)------ 一次完整请求的里里外外</a></span></h1>
<h1 class="title_txt"><span style="font-size: medium;"><a href="http://blog.csdn.net/cutesource/archive/2009/12/26/5081916.aspx">Tomcat
源码分析(三)------ 可携带状态的线程池</a></span></h1>
<h1 class="title_txt"><span style="font-size: medium;"><a href="http://blog.csdn.net/cutesource/archive/2009/12/28/5091732.aspx">Tomcat
源码分析(四)------ Request和Response处理的全过程</a></span></h1>
31 楼 lsb 2010-06-20  
    LZ 看这个有什么好的资料吗?推荐下看看。
30 楼 shmilyzyn 2010-06-07  
mark下,有时间看看
29 楼 tzm1984 2010-06-04  
mark一下,有时间也来研究下源码,就是自己太懒!
28 楼 hilor 2010-06-03  
mark下, 支持源码研究贴, 有时间来看看
27 楼 不知云 2010-06-02  
我也mark一下,以后再看。
26 楼 transist 2010-04-02  
这股认真劲值得学习
25 楼 池中物 2010-03-31  
嗯,新规范,默认request.forward是不需要经过的filte处理r的

配置了<dispatcher>FORWARD</dispatcher>就是要经过filter了
24 楼 jackdown 2010-03-31  
写的挺详细的,有空好好拜读,谢谢分享经验心得。
23 楼 luckaway 2010-01-04  
vera_sq 写道
具体情况是这样的,用户登录后,url地址栏显示的地址是http://localhost:8080/mediaServer/view/login!login.action;jsessionid=BC71820C36F64745F0E850548D4F1403,但是用户希望不要这样显示,所以当时我在struts.xml里面配置了type="redirect"。这样用户登录后,进去的url地址就是http://localhost:8080/mediaServer/view/login.jsp这样的,但是其它的action请求我并没有设置他的type属性,他就是默认的forward。

forward到*.jsp那是没问题的
22 楼 vera_sq 2010-01-04  
具体情况是这样的,用户登录后,url地址栏显示的地址是http://localhost:8080/mediaServer/view/login!login.action;jsessionid=BC71820C36F64745F0E850548D4F1403,但是用户希望不要这样显示,所以当时我在struts.xml里面配置了type="redirect"。这样用户登录后,进去的url地址就是http://localhost:8080/mediaServer/view/login.jsp这样的,但是其它的action请求我并没有设置他的type属性,他就是默认的forward。
21 楼 vera_sq 2010-01-04  
luckaway 写道
vera_sq 写道
LZ,我在web.xml配置中增加<dispatcher>FORWARD</dispatcher>这句配置后,发送请求就报404错误啊!
我的项目用的是ssh+tomcat+mysql,我用的是struts2。
想请教下是怎么回事呢?是还需要配置什么地方吗?


两个都配置起来,本来以为FORWARD是包含REQUEST。
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.htm</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
刚我试过了,可以用的


LZ,我以前好像也有遇到过你这样的问题,不过好像Sturts2.0他的默认方式是用forward的,所以也可以不用设置的。你试试呢!
20 楼 luckaway 2010-01-01  
xiaolongfeixiang 写道
luckaway 写道
网上已经有很多关于redirect和forward区别的文章,更多的都是只是一些概念上的描述,虽然在大多情况下,知道这些就已经足够了。但也有例外:forward not working for struts2,why?我也是在工作中碰到了这个问题,才特意看了下tomcat有关这部分的源代码。深刻的了解下也无妨。
redirect和forward都是属于servlet规范的,不同的servlet容器的实现可能会有一些区别,但原理都是类似的。

redirect和forward的定义:
1. redirect(重定向):服务端发送给客户端一个重定向的临时响应头,这个响应头包含重定向之后的URL,客户端用新的URL重新向服务器发送一个请求。
2. forward(请求转向):服务器程序内部请求转向,这个特性允许前一个程序用于处理请求,而后一个程序用来返回响应。

Redirect的原理比较简单,它的定义也已经描述的很清楚了,我也不想多讲什么,就贴一段简单的代码吧!
。。。。。。。。。。。。。。。。。。


赞楼主一个,想问下lz,Tomcat中SessionListener的机制。多个Session同时创建时,是形成一个队列调用session.create方法,还是各自并发地调用session.create方法?

应该需要分析源码吧?(给我点提示也可以)谢谢啦!


并发的调用session.create方法的,是设置sessionId的时候通知接听器的!!!

org.apache.catalina.session.StandardSession



  /**
     * Set the session identifier for this session.
     *
     * @param id The new session identifier
     */
    public void setId(String id) {

        if ((this.id != null) && (manager != null))
            manager.remove(this);

        this.id = id;

        if (manager != null)
            manager.add(this);
        tellNew();
    }



tellNew():
 public void tellNew() {

        // Notify interested session event listeners
        fireSessionEvent(Session.SESSION_CREATED_EVENT, null);

        // Notify interested application event listeners
        Context context = (Context) manager.getContainer();
        Object listeners[] = context.getApplicationLifecycleListeners();
        if (listeners != null) {
            HttpSessionEvent event =
                new HttpSessionEvent(getSession());
            for (int i = 0; i < listeners.length; i++) {
                if (!(listeners[i] instanceof HttpSessionListener))
                    continue;
                HttpSessionListener listener =
                    (HttpSessionListener) listeners[i];
                try {
                    fireContainerEvent(context,
                                       "beforeSessionCreated",
                                       listener);
                    listener.sessionCreated(event);
                    fireContainerEvent(context,
                                       "afterSessionCreated",
                                       listener);
                } catch (Throwable t) {
                    try {
                        fireContainerEvent(context,
                                           "afterSessionCreated",
                                           listener);
                    } catch (Exception e) {
                        ;
                    }
                    manager.getContainer().getLogger().error
                        (sm.getString("standardSession.sessionEvent"), t);
                }
            }
        }

    }



必须要保证SessionListener的实现类是线程安全的!




如果你对接听器模式不太了解的话,建议你先去学习接听器模式!
SessionListener就是接听器模式
19 楼 xiaolongfeixiang 2010-01-01  
luckaway 写道
网上已经有很多关于redirect和forward区别的文章,更多的都是只是一些概念上的描述,虽然在大多情况下,知道这些就已经足够了。但也有例外:forward not working for struts2,why?我也是在工作中碰到了这个问题,才特意看了下tomcat有关这部分的源代码。深刻的了解下也无妨。
redirect和forward都是属于servlet规范的,不同的servlet容器的实现可能会有一些区别,但原理都是类似的。

redirect和forward的定义:
1. redirect(重定向):服务端发送给客户端一个重定向的临时响应头,这个响应头包含重定向之后的URL,客户端用新的URL重新向服务器发送一个请求。
2. forward(请求转向):服务器程序内部请求转向,这个特性允许前一个程序用于处理请求,而后一个程序用来返回响应。

Redirect的原理比较简单,它的定义也已经描述的很清楚了,我也不想多讲什么,就贴一段简单的代码吧!
。。。。。。。。。。。。。。。。。。


赞楼主一个,想问下lz,Tomcat中SessionListener的机制。多个Session同时创建时,是形成一个队列调用session.create方法,还是各自并发地调用session.create方法?

应该需要分析源码吧?(给我点提示也可以)谢谢啦!
18 楼 luckaway 2009-12-31  
hanjiangit 写道
luckaway 写道


两个都配置起来,本来以为FORWARD是包含REQUEST。
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.htm</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
刚我试过了,可以用的


我照你的配了报错啦 500 错误

贴一点错误

javax.servlet.ServletException: Filter execution threw an exception
	org.apache.struts2.dispatcher.ServletDispatcherResult.doExecute(ServletDispatcherResult.java:154)
	org.apache.struts2.dispatcher.StrutsResultSupport.execute(StrutsResultSupport.java:186)
	com.opensymphony.xwork2.DefaultActionInvocation.executeResult(DefaultActionInvocation.java:361)
	com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:265)
	org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:52)
	org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:468)



你是不是tomcat版本太低了! dispatcher的特性是servlet2.4规范里加的!

tomcat版本和servlet规范的对应

http://tomcat.apache.org/whichversion.html
17 楼 hanjiangit 2009-12-31  
luckaway 写道


两个都配置起来,本来以为FORWARD是包含REQUEST。
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.htm</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
刚我试过了,可以用的


我照你的配了报错啦 500 错误

贴一点错误

javax.servlet.ServletException: Filter execution threw an exception
	org.apache.struts2.dispatcher.ServletDispatcherResult.doExecute(ServletDispatcherResult.java:154)
	org.apache.struts2.dispatcher.StrutsResultSupport.execute(StrutsResultSupport.java:186)
	com.opensymphony.xwork2.DefaultActionInvocation.executeResult(DefaultActionInvocation.java:361)
	com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:265)
	org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:52)
	org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:468)

16 楼 hanjiangit 2009-12-31  
向楼主致敬 学习 我学到东西了
15 楼 luckaway 2009-12-31  
vera_sq 写道
LZ,我在web.xml配置中增加<dispatcher>FORWARD</dispatcher>这句配置后,发送请求就报404错误啊!
我的项目用的是ssh+tomcat+mysql,我用的是struts2。
想请教下是怎么回事呢?是还需要配置什么地方吗?


两个都配置起来,本来以为FORWARD是包含REQUEST。
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.htm</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
刚我试过了,可以用的
14 楼 vera_sq 2009-12-31  
LZ,我在web.xml配置中增加<dispatcher>FORWARD</dispatcher>这句配置后,发送请求就报404错误啊!
我的项目用的是ssh+tomcat+mysql,我用的是struts2。
想请教下是怎么回事呢?是还需要配置什么地方吗?
13 楼 jelver 2009-12-31  
不错,多谢分享

相关推荐

Global site tag (gtag.js) - Google Analytics