struts的message标签换成EL表达式(国际化相关)

  对于习惯于使用EL表达式的人来说,用struts的message标签简直是种倒退。message用到地方非常多,每次都敲<bean:message key="...."/>,在这个标签污染严重的时代可不是什么好事啊。 
  幸好,将其转换为EL表达式不难。
要点: 
   EL表达式对java.util.Map对象的点运算符的递归调用。举例登录页面,${message.login.username}。
   解释一下:

引用
只要request里面有个属性名叫"message"的Map,这个Map里有个字符串"login"的key,这个Key指向一个Map,而这个Map里有个字符串"username"的key,这个key指向一个字符串。

 

 


 

而这个字符串就是我们要的Message。我们要做的就是把这些MessageResource文件中的key转换成这样的格式 ----- message.login.username。 
  唯一的缺点就是需要有一个"根"key在JSP的某个Context上下里(最好是Session,下面会讲到)。"message"就是"根"key,但我们可以让它更简便些${_msg.login.username}或者干脆${_.login.username}.这样,资源文件中的键值对就被转换到树形的Map群里了,这里我们给它个名字“资源Map”。 
  好了,还有一个需要解决的问题,国际化。如果对struts有研究的话,应该会知道用户的语言种类是记录在HttpSession "org.apache.struts.action.LOCALE"(struts的org.apache.struts.Globals.LOCALE_KEY)属性中的java.util.Locale对象。通过HttpSessionAttributeListener可以侦测到其值变化(用户改变语言的时候)。这样,只需要一个全局的Map记住所有资源文件转换而成的“资源Map”,key就是Locale.toString()。只要用户的Locale发生了变化,通过侦听器切换用户Session的“根”key指向“资源Map”即可。
废话不多说,开始行动:
第一个,先做一个读取资源文件并转换“资源Map”的类MessagePreLoader,假设你已经用上Spring了。

Java代码

  1 package com.your.struts.base.bean;
  2 
  3 import java.io.InputStream;
  4 import java.util.HashMap;
  5 import java.util.Iterator;
  6 import java.util.Map;
  7 import java.util.Properties;
  8 
  9 import org.springframework.beans.factory.InitializingBean;
 10 
 11 public class MessagePreLoader implements InitializingBean {
 12     /**
 13      * 默认资源Map对应的Key
 14      */
 15     private static final String DEFAULT_LOCAL_KEY = "default";
 16     /**
 17      * 全局的Map,所有资源Map的挂载点。
 18      */
 19     private Map resourceHolder = new HashMap();
 20     /**
 21      * struts的资源文件的位置。假设struts的配置是:
 22      * <message-resources parameter="com.your.struts.message.ApplicationResources" >
 23      * 那么此处则是com/your/struts/message/ApplicationResources
 24      * 所有“.”换成“/”
 25      */
 26     private String resourceName ;
 27     /**
 28      * 所支持的语言对象集合.
 29      */
 30     private String[] locales = {};
 31     /**
 32      * 设定资源文件是xml格式还是普通的properties文件。此处默认是xml格式(不用借助插件就可以直接用XML编辑器编辑)
 33      */
 34     private boolean xmlResouce=true;
 35 
 36     public boolean isXmlResouce() {
 37         return xmlResouce;
 38     }
 39 
 40     public void setXmlResouce(boolean xmlResouce) {
 41         this.xmlResouce = xmlResouce;
 42     }
 43 
 44     public String[] getLocales() {
 45         return locales;
 46     }
 47 
 48     public void setLocales(String[] locales) {
 49         this.locales = locales;
 50     }
 51 
 52     public String getResourceName() {
 53         return resourceName;
 54     }
 55 
 56     public void setResourceName(String resourceName) {
 57         this.resourceName = resourceName;
 58     }
 59 
 60     public Map getResourceHolder() {
 61         return resourceHolder;
 62     }
 63 
 64     private void addMap(String localeKey) throws Exception{
 65         String name=null;
 66         if(DEFAULT_LOCAL_KEY.equals(localeKey)){
 67             name=this.resourceName+(xmlResouce?".xml":".properties");
 68         }else{
 69             name=this.resourceName+"_"+localeKey+(xmlResouce?".xml":".properties");
 70         }
 71         Map map = new HashMap();
 72         this.resourceHolder.put(localeKey, map);
 73         ClassLoader classLoader =
 74             Thread.currentThread().getContextClassLoader();
 75 
 76         if (classLoader == null) {
 77             classLoader = this.getClass().getClassLoader();
 78         }
 79 
 80         InputStream is = classLoader.getResourceAsStream(name);
 81         Properties props = new Properties();
 82         if(xmlResouce){
 83             props.loadFromXML(is);//此处是因为本人项目所用的资源文件是xml,方便编辑而不必用特殊的插件。
 84         }else{
 85             props.load(is);//不用InputStreamReader的方式是因为struts资源文件本身就只使用InputStream方式。
 86         }
 87         for(Iterator iter = props.keySet().iterator();iter.hasNext();){
 88             String key = (String)iter.next();
 89             String[] strs=key.split("\\.");
 90             String value = props.getProperty(key);
 91             Map last = map;
 92             for (int j = 0; j < strs.length; j++) {
 93                 Object o=last.get(strs[j]);
 94                 if(o==null){
 95                     if(j!=strs.length-1){//不是最后一个key
 96                         o=new HashMap();
 97                         last.put(strs[j], o);
 98                         last=(Map)o;
 99                     }else{//最后一个Key
100                         last.put(strs[j], value);
101                     }
102                 }else{
103                     if(j!=strs.length-1){//不是最后一个key
104                         if(o instanceof String)throw new Exception(" is a java.lang.String ,expact java.util.Map," +
105                                 "cause by key conflict,such as: \"x.y.z\" and \"x.y\" all exists. current key:"+key);
106                         last=(Map)o;
107                     }else{//最后一个Key
108                         throw new Exception(" dulpicat key exists , current key:"+key);//此类情况应该永远不会发生
109                     }
110                 }
111             }
112         }
113     }
114     
115     public void afterPropertiesSet() throws Exception {
116         this.addMap(DEFAULT_LOCAL_KEY);
117         for (int i = 0; i < locales.length; i++) {
118             this.addMap(locales[i]);
119         }
120     }
121     
122     public Map getLocaleMessageSet(String localeKey){
123         Object obj=this.resourceHolder.get(localeKey);
124         return (Map)(obj==null?this.resourceHolder.get(DEFAULT_LOCAL_KEY):obj);
125     }
126     

Spring 配置:

Xml代码
 1   <bean id="messagePreLoader" class="com.your.struts.base.bean.MessagePreLoader">
 2       <property name="xmlResouce" value="false"></property>
 3       <property name="resourceName" value="com/your/struts/message/ApplicationResources"></property>
 4       <property name="locales">
 5           <list>
 6               <value>zh_CN</value>
 7               <value>en</value>
 8           </list>
 9       </property>
10   </bean>

 

制作SessionListener的委派类:
SessionListenerDelegate用于侦听locale信息的改变。

 

Java代码
 1 package com.your.servlet.listener;
 2 
 3 import java.util.Locale;
 4 
 5 import javax.servlet.http.HttpSession;
 6 import javax.servlet.http.HttpSessionAttributeListener;
 7 import javax.servlet.http.HttpSessionBindingEvent;
 8 
 9 import org.apache.commons.logging.Log;
10 import org.apache.commons.logging.LogFactory;
11 import org.apache.struts.Globals;
12 import org.springframework.context.ApplicationContext;
13 import org.springframework.web.context.support.WebApplicationContextUtils;
14 
15 import com.your.struts.base.bean.MessagePreLoader;
16 
17 public class SessionListenerDelegate implements HttpSessionAttributeListener{
18     /**
19      * Session里面资源Map的Key
20      */
21     public static final String MESSAGE_SESSION_KEY="_msg";
22 
23 
24     public SessionListenerDelegate() {
25         super();
26     }
27 
28     private static final Log logger = LogFactory.getLog(SessionListenerDelegate.class);
29     /**
30      * 侦测语言属性的变化,并设置用户对应的资源Map
31      * @param bindingEvent
32      */
33     private void setLocale(HttpSessionBindingEvent bindingEvent) {
34         if(bindingEvent.getName().equals(Globals.LOCALE_KEY)){
35             HttpSession session=bindingEvent.getSession();
36             Locale locale = (Locale)session.getAttribute(Globals.LOCALE_KEY);
37             ApplicationContext ctx = (ApplicationContext) WebApplicationContextUtils.getRequiredWebApplicationContext(session.getServletContext());
38             MessagePreLoader messagePreLoader = (MessagePreLoader) ctx.getBean("messagePreLoader");
39             String localeKey=null;
40             if(locale!=null){
41                 localeKey=locale.toString();
42             }
43             Object localeMsgSet= messagePreLoader.getLocaleMessageSet(localeKey);
44             session.setAttribute(MESSAGE_SESSION_KEY, localeMsgSet);
45         }
46     }
47     
48     public void attributeAdded(HttpSessionBindingEvent bindingEvent) {
49         setLocale(bindingEvent);
50         if(logger.isDebugEnabled())logger.debug("attribute added. name:{"+bindingEvent.getName()+"} old value:"+bindingEvent.getValue()+" new value:"+bindingEvent.getSession().getAttribute(bindingEvent.getName()));
51     }
52 
53 
54     public void attributeRemoved(HttpSessionBindingEvent bindingEvent) {
55         if(logger.isDebugEnabled())logger.debug("attribute remove. name:{"+bindingEvent.getName()+"} value:"+bindingEvent.getValue());
56     }
57 
58     public void attributeReplaced(HttpSessionBindingEvent bindingEvent) {
59         this.setLocale(bindingEvent);
60         if(logger.isDebugEnabled())logger.debug("attribute replace. name:{"+bindingEvent.getName()+"} old value:"+bindingEvent.getValue()+" new value:"+bindingEvent.getSession().getAttribute(bindingEvent.getName()));
61     }
62 
63 }

web.xml里面:

Xml代码
1 <listener>
2   <listener-class>
3     com.your.servlet.listener.SessionListenerDelegate
4   </listener-class>
5 </listener>    

这样。我们就可以在JSP中使用${_msg....}这样的EL表达式来访问国际化的资源信息了。

Html代码
1  <table>
2         <tr><td>${_msg.commons.login.username}:</td><td><input type='text' name='j_username' value='<c:if test="${not empty param.login_error}"><c:out value="${lastUserName}"/></c:if>'/></td></tr>
3         <tr><td>${_msg.commons.login.password}:</td><td><input type='password' name='j_password'></td></tr>
4  </table>

没有了message标签,输入也方便了。何乐不为?
最后,需要注意的地方,你的message文件中不能出现这样的打架现象:

properties代码
1 commons.login=User Login
2 commons.login.username=User Name

这样小小的请求,我想你是可以忍受的。
另外,如果觉得这个方式对你项目没有帮助,请勿使用。。。

谢谢原作者,学习了,转自:http://llade.iteye.com/blog/264372

posted on 2013-07-23 16:31  013  阅读(975)  评论(0编辑  收藏  举报