`

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了。
package com.your.struts.base.bean;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

import org.springframework.beans.factory.InitializingBean;

public class MessagePreLoader implements InitializingBean {
	/**
	 * 默认资源Map对应的Key
	 */
	private static final String DEFAULT_LOCAL_KEY = "default";
	/**
	 * 全局的Map,所有资源Map的挂载点。
	 */
	private Map resourceHolder = new HashMap();
	/**
	 * struts的资源文件的位置。假设struts的配置是:
	 * <message-resources parameter="com.your.struts.message.ApplicationResources" >
	 * 那么此处则是com/your/struts/message/ApplicationResources
	 * 所有“.”换成“/”
	 */
	private String resourceName ;
	/**
	 * 所支持的语言对象集合.
	 */
	private String[] locales = {};
	/**
	 * 设定资源文件是xml格式还是普通的properties文件。此处默认是xml格式(不用借助插件就可以直接用XML编辑器编辑)
	 */
	private boolean xmlResouce=true;

	public boolean isXmlResouce() {
		return xmlResouce;
	}

	public void setXmlResouce(boolean xmlResouce) {
		this.xmlResouce = xmlResouce;
	}

	public String[] getLocales() {
		return locales;
	}

	public void setLocales(String[] locales) {
		this.locales = locales;
	}

	public String getResourceName() {
		return resourceName;
	}

	public void setResourceName(String resourceName) {
		this.resourceName = resourceName;
	}

	public Map getResourceHolder() {
		return resourceHolder;
	}

	private void addMap(String localeKey) throws Exception{
		String name=null;
		if(DEFAULT_LOCAL_KEY.equals(localeKey)){
			name=this.resourceName+(xmlResouce?".xml":".properties");
		}else{
			name=this.resourceName+"_"+localeKey+(xmlResouce?".xml":".properties");
		}
		Map map = new HashMap();
        this.resourceHolder.put(localeKey, map);
		ClassLoader classLoader =
            Thread.currentThread().getContextClassLoader();

        if (classLoader == null) {
            classLoader = this.getClass().getClassLoader();
        }

        InputStream is = classLoader.getResourceAsStream(name);
        Properties props = new Properties();
        if(xmlResouce){
        	props.loadFromXML(is);//此处是因为本人项目所用的资源文件是xml,方便编辑而不必用特殊的插件。
        }else{
        	props.load(is);//不用InputStreamReader的方式是因为struts资源文件本身就只使用InputStream方式。
        }
        for(Iterator iter = props.keySet().iterator();iter.hasNext();){
        	String key = (String)iter.next();
        	String[] strs=key.split("\\.");
        	String value = props.getProperty(key);
        	Map last = map;
        	for (int j = 0; j < strs.length; j++) {
				Object o=last.get(strs[j]);
				if(o==null){
					if(j!=strs.length-1){//不是最后一个key
						o=new HashMap();
						last.put(strs[j], o);
						last=(Map)o;
					}else{//最后一个Key
						last.put(strs[j], value);
					}
				}else{
					if(j!=strs.length-1){//不是最后一个key
						if(o instanceof String)throw new Exception(" is a java.lang.String ,expact java.util.Map," +
								"cause by key conflict,such as: \"x.y.z\" and \"x.y\" all exists. current key:"+key);
						last=(Map)o;
					}else{//最后一个Key
						throw new Exception(" dulpicat key exists , current key:"+key);//此类情况应该永远不会发生
					}
				}
			}
        }
	}
	
	public void afterPropertiesSet() throws Exception {
		this.addMap(DEFAULT_LOCAL_KEY);
		for (int i = 0; i < locales.length; i++) {
			this.addMap(locales[i]);
		}
	}
	
	public Map getLocaleMessageSet(String localeKey){
		Object obj=this.resourceHolder.get(localeKey);
		return (Map)(obj==null?this.resourceHolder.get(DEFAULT_LOCAL_KEY):obj);
	}
	



Spring 配置:
  <bean id="messagePreLoader" class="com.your.struts.base.bean.MessagePreLoader">
  	<property name="xmlResouce" value="false"></property>
  	<property name="resourceName" value="com/your/struts/message/ApplicationResources"></property>
  	<property name="locales">
  		<list>
  			<value>zh_CN</value>
  			<value>en</value>
  		</list>
  	</property>
  </bean>

制作SessionListener的委派类:
SessionListenerDelegate用于侦听locale信息的改变。
package com.your.servlet.listener;

import java.util.Locale;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.Globals;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.your.struts.base.bean.MessagePreLoader;

public class SessionListenerDelegate implements HttpSessionAttributeListener{
	/**
	 * Session里面资源Map的Key
	 */
	public static final String MESSAGE_SESSION_KEY="_msg";


	public SessionListenerDelegate() {
		super();
	}

	private static final Log logger = LogFactory.getLog(SessionListenerDelegate.class);
	/**
	 * 侦测语言属性的变化,并设置用户对应的资源Map
	 * @param bindingEvent
	 */
	private void setLocale(HttpSessionBindingEvent bindingEvent) {
		if(bindingEvent.getName().equals(Globals.LOCALE_KEY)){
			HttpSession session=bindingEvent.getSession();
			Locale locale = (Locale)session.getAttribute(Globals.LOCALE_KEY);
			ApplicationContext ctx = (ApplicationContext) WebApplicationContextUtils.getRequiredWebApplicationContext(session.getServletContext());
			MessagePreLoader messagePreLoader = (MessagePreLoader) ctx.getBean("messagePreLoader");
			String localeKey=null;
			if(locale!=null){
				localeKey=locale.toString();
			}
			Object localeMsgSet= messagePreLoader.getLocaleMessageSet(localeKey);
			session.setAttribute(MESSAGE_SESSION_KEY, localeMsgSet);
		}
	}
	
	public void attributeAdded(HttpSessionBindingEvent bindingEvent) {
		setLocale(bindingEvent);
		if(logger.isDebugEnabled())logger.debug("attribute added. name:{"+bindingEvent.getName()+"} old value:"+bindingEvent.getValue()+" new value:"+bindingEvent.getSession().getAttribute(bindingEvent.getName()));
	}


	public void attributeRemoved(HttpSessionBindingEvent bindingEvent) {
		if(logger.isDebugEnabled())logger.debug("attribute remove. name:{"+bindingEvent.getName()+"} value:"+bindingEvent.getValue());
	}

	public void attributeReplaced(HttpSessionBindingEvent bindingEvent) {
		this.setLocale(bindingEvent);
		if(logger.isDebugEnabled())logger.debug("attribute replace. name:{"+bindingEvent.getName()+"} old value:"+bindingEvent.getValue()+" new value:"+bindingEvent.getSession().getAttribute(bindingEvent.getName()));
	}

}



web.xml里面:
<listener>
  <listener-class>
    com.your.servlet.listener.SessionListenerDelegate
  </listener-class>
</listener>	


这样。我们就可以在JSP中使用${_msg....}这样的EL表达式来访问国际化的资源信息了。
 <table>
        <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>
        <tr><td>${_msg.commons.login.password}:</td><td><input type='password' name='j_password'></td></tr>
 </table>


没有了message标签,输入也方便了。何乐不为?

最后,需要注意的地方,你的message文件中不能出现这样的打架现象:
commons.login=User Login
commons.login.username=User Name


这样小小的请求,我想你是可以忍受的。

另外,如果觉得这个方式对你项目没有帮助,请勿使用。。。
分享到:
评论
3 楼 言日星极 2012-03-29  
谢谢解答,看来直接用c:out就可以防止javascript脚本攻击了,以前还是傻傻在后台过滤<script>。

2 楼 llade 2012-03-29  
言日星极 写道
<td>
    <input type='text' name='j_username' 
        value='<c:if test="${not empty param.login_error}">
                                <c:out value="${lastUserName}"/>
               </c:if>'/>
</td>


<b>
使用c:out标签输出要比直接使用${lastUserName}效率要高吗?还是因为只是为了程序的可读性。
</b>

<c:out value="${lastUserName}"/>

假如lastUserName 有"<td/>"这类Html标签,直接用${lastUserName}会破坏掉页面HTML标签。

用c:out,会自动把HTML标签的尖括号转换为&lt;和&gt;实体。保证了页面完整。

只是处理手段。
1 楼 言日星极 2012-03-29  
<td>
    <input type='text' name='j_username' 
        value='<c:if test="${not empty param.login_error}">
                                <c:out value="${lastUserName}"/>
               </c:if>'/>
</td>


<b>
使用c:out标签输出要比直接使用${lastUserName}效率要高吗?还是因为只是为了程序的可读性。
</b>

相关推荐

    jsp,struts,EL,session等注意总结集合

    EL表达式通常在${}内编写,可以用来获取、设置值,或者进行基本的算术和逻辑操作。EL与JSP的脚本元素相比,更易读,更易于维护,减少了页面中的Java代码量。 【Session】 Session是Web应用程序中的一种会话管理机制...

    Struts2常用标签

    在实际使用中,结合OGNL表达式语言,Struts2标签可以非常灵活地处理各种复杂情况。例如,我们可以利用OGNL动态地计算条件、访问对象属性等。总的来说,掌握Struts2的标签库对于提高Struts2应用的开发质量和效率至关...

    struts2标签库 struts2标签库

    它们是预定义的JSP标签,可以替代复杂的JSP脚本和EL表达式,使得页面逻辑更加清晰。 1. **数据展示标签**: - `s:property`:用于显示模型对象的属性值。 - `s:label`:创建一个标签,通常与表单元素一起使用,...

    struts标签中文文档

    - 表达式语言(EL):Struts标签通常结合EL表达式来获取或设置值。 - 事件处理:动作标签可以监听和处理JSP页面上的事件,如按钮点击。 4. "使用方法.txt"可能包含的内容: - 标签导入:如何在JSP页面中正确引入...

    Struts中Bean标签的应用

    接着通过`&lt;bean:size&gt;`标签计算数组的长度,并通过EL表达式输出结果。 同样,也可以用来计算集合或映射的长度: ```jsp Collection&lt;String&gt; fruit = new ArrayList(); fruit.add("苹果"); fruit.add("橘子")...

    struts常用标签及其用法归纳

    7. **表达式语言(EL)和JSTL**: - 虽然不是Struts自带的标签,但与Struts结合使用可以增强功能。例如,EL(Expression Language)用于在JSP中简便地访问JavaBean属性,JSTL(JavaServer Pages Standard Tag ...

    struts2学习心得

    本文总结了Struts2的常用方法和经验,涵盖了配置问题、EL表达式、Struts2的架构和Namespace等方面的知识点。 一、配置问题 Struts2的最新版本为GA 2.2.1,需要以下几个类库: 1.commons-fileupload-1.2.1.jar 2....

    struts标签手册

    ### Struts标签手册详解 #### 一、Bean Tags:Bean操作与管理的基石 Bean Tags是Struts框架中用于处理JavaBean对象的核心组件,提供了创建、访问和操作bean的强大功能。这一系列的标签使得开发者能够更高效地在JSP...

    struts学习笔记

    总之,Struts框架结合JSTL和EL表达式,为Java Web开发提供了强大且易用的工具集,使开发者能够更加专注于业务逻辑,而非底层的表示层细节。通过熟练掌握这些工具,初学者可以快速提升Web应用开发能力。

    struts5类标签

    它等同于使用EL表达式`${}`或`&lt;%= %&gt;`。例如: ```jsp ``` 3. `&lt;bean:message/&gt;`:这个标签用于读取属性的静态文本内容,支持国际化(i18n)。首先,你需要在类路径下创建资源文件,如`...

    struts的九个必备常用包

    7. **struts-el**:支持表达式语言(EL,Expression Language)的标签库,EL是一种轻量级的脚本语言,常用于JSP页面中,用于访问JavaBean属性或执行简单的运算。 8. **struts-faces**:如果项目中使用了JSF(Java...

    struts2的s标签

    S标签的设计目标是减少对脚本语言(如JSP EL表达式或脚本let)的依赖,并且提高页面的可读性和维护性。 #### 二、S标签的使用与配置 要在JSP页面中使用Struts2的S标签,首先需要在页面顶部添加如下声明: ```jsp ...

    struts国际化(项目)

    例如,假设我们有一个键为`greeting`的资源,我们可以在JSP页面中使用EL表达式来显示: ```jsp &lt;fmt:message key="greeting"/&gt; ``` 或者在Action类中: ```java ActionContext context = ActionContext.getContext()...

    struts笔记

    4. **EL表达式**:还可以通过EL表达式来获取国际化文本。 ```jsp ${getText("hello")} ``` #### 七、CSRF防护 为了防止跨站请求伪造(CSRF)攻击,Struts2提供了内置的Token拦截器。 1. **添加Token标签**:在...

    Struts1.3和config配置详解

    这些JAR文件如struts-core.jar、struts-el.jar、struts-taglib.jar等,提供了框架的核心功能、表达式语言支持以及标签库。开发者可以将这些库添加到项目的类路径中,以便使用Struts框架进行开发。 通过深入理解`...

    struts 2 action 动态调用

    在JSP页面中,可以使用EL表达式来获取Action中设置的值。 ```jsp ; charset=UTF-8" pageEncoding="UTF-8"%&gt; 动态方法调用 ${message} ``` #### 五、总结 本文详细介绍了Struts 2框架中Action动态...

    struts2属性文件struts.xml的配置

    - **`${oneInfor.getUserId()}`**:EL表达式,动态获取`oneInfor`对象的`getUserId()`方法返回值。 #### 六、示例分析 以下是一个具体的Action配置示例: ```xml &lt;result name="query"&gt;/oneInformation.jsp ``...

    Struts国际化录象教程

    3. **在JSP中使用**:在JSP页面中,你可以通过`&lt;bean:message&gt;`标签或者EL表达式(`${}`)来获取资源包中的字符串。标签的`key`属性应与资源包中键对应,Struts会自动根据用户浏览器的locale选择正确的资源包。 4. ...

Global site tag (gtag.js) - Google Analytics