`
sei_ljf
  • 浏览: 59771 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

深入Struts2的过滤器FilterDispatcher--中文乱码及字符编码过滤器

阅读更多
前几天在论坛上看到一篇帖子,是关于Struts2.0中文乱码的,楼主采用的是spring的字符编码过滤器(CharacterEncodingFilter)统一编码为GBK,前台提交表单数据到Action,但是在Action中得到的中文全部是乱码,前台的页面编码都是GBK没有问题。这是为什么呢?下面我们就通过阅读FilterDispatcher和CharacterEncodingFilter这两个过滤器的源代码,了解其实现细节,最终得出为什么中文还是乱码!


测试环境及其前置知识
Struts2.0.14
Spring2.5.6
Eclipse3.4
Filter的相关知识,尤其要知道Filter的执行顺序是按照web.xml中配置的filter-mapping顺序执行的。

web.xml定义文件
这里直接采用那篇帖子的web配置

<!-- spring字符集过滤器 --> 
  <filter> 
  <filter-name>CharacterEncoding</filter-name> 
  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> 
  <init-param> 
  <param-name>encoding</param-name> 
  <param-value>GBK</param-value> 
  </init-param> 
  <init-param> 
  <param-name>forceEncoding</param-name> 
  <param-value>true</param-value> 
  </init-param> 
  </filter> 
  <filter> 
     <filter-name>Struts2</filter-name> 
     <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> 
  </filter> 
  <filter-mapping> 
      <filter-name>CharacterEncoding</filter-name> 
      <url-pattern>*.action</url-pattern> 
  </filter-mapping> 
  <filter-mapping> 
     <filter-name>Struts2</filter-name> 
     <url-pattern>*.action</url-pattern> 
  </filter-mapping> 

分析过滤器源代码,找出为什么
根据filter的执行顺序知,会先执行CharacterEncoding过滤器,再执行Struts2过滤器。
CharacterEncodingFilter的核心doFilterInternal方法如下:
protected void doFilterInternal(  
            HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)  
            throws ServletException, IOException {  
  
if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {  
            request.setCharacterEncoding(this.encoding);//设置字符集编码  
            if (this.forceEncoding && responseSetCharacterEncodingAvailable) {  
                response.setCharacterEncoding(this.encoding);  
            }  
        }  
        filterChain.doFilter(request, response);//激活下一个过滤器  
    }  

很简洁,只要this.encoding != null就会设置request的字符集编码,this.encoding就是web.xml中CharacterEncoding过滤器配置的encoding为GBK。
到这里我们已经执行了一个Filter(CharacterEncoding)已经把request的字符集设置为GBK,然后执行filterChain.doFilter(request, response);//激活下一个过滤器即我们定义的Struts2过滤器。
到这里request的字符集编码还是GBK,但是我们在Action中取得的中文为乱码,使用request.getCharacterEncoding()获取的编码为UTF-8,那么我们可以肯定问题出在FilterDispatcher过滤器上。查看FilterDispatcher的源代码,在其doFilter方法里找到了prepareDispatcherAndWrapRequest方法,看其名字是对request进行预处理和封装的方法。
 
protected HttpServletRequest prepareDispatcherAndWrapRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException {  
  
        Dispatcher du = Dispatcher.getInstance();  
  
        if (du == null) {  
  
            Dispatcher.setInstance(dispatcher);  
            dispatcher.prepare(request, response);//设置编码的关键地方  
        } else {  
            dispatcher = du;  
        }  
        //省略一些代码  
  
        return request;  
    }  

展开dispatcher.prepare(request, response)发现:

public void prepare(HttpServletRequest request, HttpServletResponse response) {  
        String encoding = null;  
        if (defaultEncoding != null) {  
            encoding = defaultEncoding;  
        }  
  
       //省略了一些代码  
  
        if (encoding != null) {  
            try {  
                request.setCharacterEncoding(encoding);//设置了字符集编码  
            } catch (Exception e) {  
                LOG.error("Error setting character encoding to '" + encoding + "' - ignoring.", e);  
            }  
        }  
    //省略了一些代码  
    }  

可以发现FilterDispatcher过滤器设置了request的字符编码,值来自defaultEncoding(看上面的代码),而defaultEncoding则是通过struts的配置文件取得的,即struts.i18n.encoding的属性值。
@Inject(StrutsConstants.STRUTS_I18N_ENCODING)  
   public static void setDefaultEncoding(String val) {  
       defaultEncoding = val;  
   }  


如果没有配置struts.i18n.encoding的值,默认是UTF-8.现在我们明白为什么中文是乱码了,也明白了为什么在Action中获取的编码是UTF-8啦。解决方法也很简单,在struts.xml文件中配置好struts.i18n.encoding的值为GBK即可,可以选择是否去掉spring的编码过滤器。
Xml代码
<constant name="struts.i18n.encoding" value="gbk"></constant> 

延伸--过滤器的其他一些思考
到了这里按说我们已经解决了问题,应该没有什么疑问了,但是前面说了,过滤器是按顺序执行的,那么我们把spring的字符过滤器放在struts的过滤器后面行不行呢,想想是可以的,因为先执行struts的过滤器,设置编码为UTF-8,然后执行spring的过滤器设置成GBK。但是实际上不是那么回事,在实际调试过程中spring的过滤器压根就没有执行,为什么呢?接着看FilterDispatcher的doFilter方法

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {  
  
  
        HttpServletRequest request = (HttpServletRequest) req;  
        HttpServletResponse response = (HttpServletResponse) res;  
        ServletContext servletContext = getServletContext();  
  
        String timerKey = "FilterDispatcher_doFilter: ";  
        try {  
            UtilTimerStack.push(timerKey);  
            request = prepareDispatcherAndWrapRequest(request, response);  
            ActionMapping mapping;  
            try {  
                mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());//关键,获取Action的映射配置  
            } catch (Exception ex) {  
                LOG.error("error getting ActionMapping", ex);  
                dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);  
                return;  
            }  
  
            if (mapping == null) {  
               //走到这里说明请求的不是一个action  
                String resourcePath = RequestUtils.getServletPath(request);  
  
                if ("".equals(resourcePath) && null != request.getPathInfo()) {  
                    resourcePath = request.getPathInfo();  
                }  
  
                if (serveStatic && resourcePath.startsWith("/struts")) {  
                    findStaticResource(resourcePath, findAndCheckResources(resourcePath), request, response);  
                } else {//很普通的一个request(非Action,非struts的静态资源)  
                    chain.doFilter(request, response);//激活下一个过滤器  
                }  
                // The framework did its job here  
                return;  
            }  
            //调用action  
            dispatcher.serviceAction(request, response, servletContext, mapping);  
  
        } finally {  
            try {  
                ActionContextCleanUp.cleanUp(req);  
            } finally {  
                UtilTimerStack.pop(timerKey);  
            }  
        }  
    }  


看上面的代码mapping=actionMapper.getMapping(request,dispatcher.getConfigurationManager());这个是得到当前请求Action的信息,比如Action的名字,命名空间,result值等,只要这个mapping不为null,过滤器就会直接执行action而不会激活下一个过滤器,这就会使得spring的那个过滤器起不了作用。那么什么时候才会激活下一个过滤器呢?答案是一个很普通的请求,多么普通呢?
不能是一个存在action。
serveStatic(struts.serve.static配置项值)为true时,不能是一个相对路径以"/struts"开头的请求,如(/struts.html,/struts/index.html),
因为这样过滤器会认为你在找struts内部的静态资源,谈后它会去诸如struts的模板文件夹下去找这些静态资源。
必须是一个类似于/index.html,/news/index.html这样的请求或者serveStatic为false时请求一个不存在的action。

当满足以上条件时才会激活下一个过滤器。看来这限制还挺多的,所以这就提出来一个注意事项了,当你在web.xml配置多个过滤器的时候,一定要把struts的过滤器放到最后,这样可以防止过滤器链被中断,导致你配置的其他过滤器不起作用。
分享到:
评论
2 楼 sei_ljf 2012-02-08  
mizhihua 写道
多谢楼主,学习了。

呵呵 转载别人的 共同学习~
1 楼 mizhihua 2012-02-08  
多谢楼主,学习了。

相关推荐

    struts核心过滤器FilterDispatcher介绍

    struts核心过滤器FilterDispatcher介绍,struts核心过滤器FilterDispatcher介绍

    解决struts2下载异常的jar包 struts2-sunspoter-stream-1.0.jar

    505) at org.apache.struts2.dispatcher.FilterDispatcher.doFilter(FilterDispatcher.java:395) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) at org....

    org.apache.struts2.dispatcher.FilterDispatcher

    org.apache.struts2.dispatcher.FilterDispatcher

    Struts2整合SiteMesh技巧

    注意ActionContextCleanUp过滤器必须在FilterDispatcher之前配置,ActionContextCleanUp的主要功能是通知FilterDispatcher执行完毕不要清除ActionContext,以便sitemesh装饰器可以访问Struts值堆栈。 配置装饰器 ...

    新struts2+jQuery所需包.rar

    &lt;filter-class&gt;org.apache.struts2.dispatcher.FilterDispatcher&lt;/filter-class&gt;, 新版本中都是过时的了,采取该过滤器, &lt;filter-class&gt;org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter...

    Struts2执行流程

    Struts2执行流程 1. web.xml 部署描述符 2. FilterDispatcher 实现StrutsStatics, Filter接口 (1)Filter:一个filter是一个对象用于执行过滤任务为每个请求资源(一个servlet或静态内容),或响应一个资源,或两者.过滤...

    Struts2基本原理

    Struts 2框架本身大致可以分为3个部分:核心控制器FilterDispatcher、业务控制器Action和用户实现的企业业务逻辑组件。 核心控制器FilterDispatcher是Struts 2框架的基础,包含了框架内部的控制流程和处理机制。...

    struts2流程与流程图

    一个请求在Struts 2框架中的处理大概分为以下几个步骤。...Struts 2的核心控制器是FilterDispatcher,有3个重要的方法:destroy()、doFilter()和Init(),可以在Struts 2的下载文件夹中找到源代码,如代码1所示。

    Struts2的工作原理和流程

    2 这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin) 3 接着FilterDispatcher被调用,...

    struts2开发文档

    ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框 架的集成很有帮助,例如:SiteMesh Plugin) 3 接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来 决定这个请是否需要调用某个...

    Struts2详细工作流程

    Struts 2框架本身大致可以分为3个部分:核心控制器FilterDispatcher、业务控制器Action和用户实现的企业业务逻辑组件。 3.1.1 核心控制器FilterDispatcher 核心控制器FilterDispatcher是Struts 2框架的基础,包含...

    SSH的jar包.rar

    2、这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin) 3、接着FilterDispatcher被调用,...

    Struts 2详细工作流程

    Struts 2框架本身大致可以分为3个部分:核心控制器FilterDispatcher、业务控制器Action和用户实现的企业业务逻辑组件。 3.1.1 核心控制器FilterDispatcher 核心控制器FilterDispatcher是Struts 2框架的基础,...

    用Struts2新建一个应用的方法步骤

    用Struts2新建一个应用的方法步骤用Struts2新建一个应用的方法步骤: 1.首先新建一个Web Project。File——new——Web Project,在Project Name中输入一个合法的名字。例如:struts2 2. 单击采单Window——...

    Java Struts 实现拦截器

    Struts2的处理流程: • 客户端产生一个HttpServletRequest的请求,该请求被提交到一系列的标准过滤器(Filter)组建链中(如ActionContextCleanUp:它主要是清理当前线程的ActionContext、Dispatcher,...

    Struts2 基本流程

    我们已在前面学习了Servlet 数据库应用,有了JSP、Servlet 、JDBC的一些知识、理解和应用,也具有了一些MVC...3. 了解核心控制器FilterDispatcher及在web.xml中的配置 4. 了解业务控制器Action及在struts.xml中的配置

    Struts2请求处理流程及源码分析

    b)根据Web.xml配置,请求首先经过ActionContextCleanUp过滤器,其为可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助(SiteMeshPlugin),主要清理当前线程的ActionContext和Dispatcher;c)请求经过插件...

    Struts2执行原理

    首先客户端发送HttServletErquest的请求,这个请求经过一系列的过滤器链(Filter),这里是有顺序的,首先经过ActionContext CleanUp,然后再经过其他过滤器(Othter Filters、SiteMesh等),最后再到FilterDispatcher

    Struts2 入门培训

    4 1.3.2. 受控目录 5 2. 入门例子 5 2.1. 项目 5 2.2. 在WEB.XML中,配置FILTERDISPATCHER过滤器 6 2.3. 配置STRUTS.PROPERTIES 6 2.4. 编写一个简单的ACTION类 7 2.5. STRUTS.XML配置文件 8...

Global site tag (gtag.js) - Google Analytics