测试了sitemesh装饰器的使用,在Tomcat6.0下部署时页面显示正常,但是迁移到Weblogic10.3.4下,被装饰器修饰的目标页面中文出现乱码,效果如下:
在网上搜索解决方法,但是都不太满意,后来查看sitemesh的源码,分别在Tomcat6.0和Weblogic10.3下调试发现运行方式不一样,在此记录一下这几天辛苦查找历程。
我的目标页面:index.jsp(已设定页面编码方式UTF-8)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <div style="background-color:yellow">测试SiteMesh</div>11 </body> </html>
装饰器页面:decorator.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator" %> <html> <head> <title>My Site - <decorator:title default="Welcome!" /></title> <decorator:head/> </head> <body> <decorator:body/> <p><small>使用了sitemesh页面装饰器</small></p> </body> </html>
配置文件:decorators.xml
<?xml version="1.0" encoding="UTF-8"?> <decorators defaultdir="/decorators"> <decorator name="main" page="decorator.jsp"> <pattern>/*</pattern> </decorator> </decorators>
web.xml(项目配置了struts2,不过这里对sitemesh没有影响,只是记录一下过滤器的排列顺序)
<!-- 清除ActionContext --> <filter> <filter-name>struts-cleanup</filter-name> <filter-class>org.apache.struts2.dispatcher.ActionContextCleanUp</filter-class> </filter> <!-- Character Encoding filter --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <!-- 页面装饰器siteMesh --> <filter> <filter-name>sitemesh</filter-name> <filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class> </filter> <!-- struts2的核心过滤器 --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> </filter> <!-- 过滤器链, 以filter-mapping的顺序决定过滤器链的顺序 --> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- ActionContextCleanUp放在SiteMesh之前 --> <filter-mapping> <filter-name>struts-cleanup</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- sitemesh处于第二位 --> <filter-mapping> <filter-name>sitemesh</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- struts过滤器放在sitemesh后面 --> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping>
在网上提供的解决方式大概有以下几种:
1、修改domains\mydomain\bin\setDomainEnv.cmd,在set JAVA_OPTIONS=%JAVA_OPTIONS%后面添加 -Dfile.encoding=UTF-8,这样被装饰页面中文乱码的问题解决了,但是weblogic控制台中文信息出现乱码,这样多少让人感觉有点不太舒服。
2、修改sitemesh源代码,com.opensymphony.module.sitemesh.filter.TextEncoder.java,将private static final String DEFAULT_ENCODING = System.getProperty("file.encoding");修改为private static final String DEFAULT_ENCODING =”UTF-8”。
3、自己创建一个Listener,该Listener的关键代码为:Properties prop = System.getProperties();prop.put("file.encoding", "utf-8");接着在web.xml中配置该Listener。Listener的作用和方式一类似,只不过该设置只对应用有效,Weblogic控制台也不会出现乱码。
在Eclipse下调试发现SiteMeshFilter执行到private Content obtainContent(ContentProcessor contentProcessor, SiteMeshWebAppContext webAppContext,HttpServletRequest request, HttpServletResponse response, FilterChain chain)方法的chain.doFilter(request, contentBufferingResponse);时,会调用ContentBufferingResponse类初始化时构造的PageResponseWrapper 示例的public void setContentType(String contentType)方法,同时传递页面类型参数。Tomcat6.0和Weblogic的区别就在这里,Tomcat6.0只执行一次,传递的参数是“text/html; charset=UTF-8”,而Weblogic执行了三次,第一次传递参数是“text/html”,其余两次则是“text/html; charset=UTF-8”,因为在第一次执行setContentType(String contentType)方法时会初始化一个com.opensymphony.module.sitemesh.filter.Buffer类的实例buffer,由于Weblogic传递参数是“text/html”,导致buffer实例的encoding属性为null,从而后续取内容调用buffer.getContents()时其执行了return TEXT_ENCODER.encode(bufferedStream.toByteArray(), encoding);(其中encoding 参数为null),进而使用了com.opensymphony.module.sitemesh.filter.TextEncoder类的DEFAULT_ENCODING 常量,方式二的修改正是基于这种原因。修改TextEncoder的DEFAULT_ENCODING 常量是可行的,但是我想进一步查找为什么Weblogic第一次执行public void setContentType(String contentType)方法传递参数是“text/html”,后来发现调用setContentType(String contentType)方法的是weblogic.jar包里weblogic.servlet.internal.ServletStubImpl类的public void execute(ServletRequest paramServletRequest, ServletResponse paramServletResponse, FilterChainImpl paramFilterChainImpl)方法有一段代码:
if (getDefaultContentType() != null) { paramServletResponse.setContentType(getDefaultContentType()); }
而getDefaultContentType()的定义是 protected String getDefaultContentType() { return null; }
所以才会造成没有设置编码的结果吧,这应该是Weblogic的一个bug,在此记录一下。