MessageStoreInterceptor 拦截器的使用
本文将详细介绍 MessageStoreInterceptor, 包括 MessageStoreInterceptor 的作用, operationMode 参数的取值, 应用举例和源码分析.
MessageStoreInterceptor 拦截器能保证 actionMessage 在 redirect 后不会丢失. struts.xml 文件的部分配置如下:
<interceptors> <interceptor-stack name="storeMessage"> <interceptor-ref name="defaultStack" /> <interceptor-ref name="store"> <param name="operationMode">AUTOMATIC</param> </interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="storeMessage" />
问题:
1. MessageStoreIntercetor 的作用是什么? 2. operationMode 可以取哪些值? 每个值都有什么作用?3. MessageStoreIntercetor 的主要应用场景是什么?4. MessageStoreIntercetor 工作的原理怎样?
1. MessageStoreInterceptor 的配置在 struts-default.xml 文件中可以找到:
<interceptor name="store" />
由 MessageStoreInterceptor 的 API 可以知道: 若 Action 类实现了 ValidationAware 接口, MessageStoreInterceptor 拦截器可以把和该 Action 相关的 messages, errors 和 field errors(下称 "消息") 保存到 session 中(默认放在 Action 中, 而 Action 在 request 中). 以使可以跨请求访问 messages, errors 和 field errors.
2. 从文档上知道 operationMode 的合法取值有: NONE, STORE, RETRIEVE 和 AUTOMATIC. 其中 NONE 为默认值.
①. STORE: MessageStoreInterceptor 拦截器会把 "消息" 放入 session 域中.
②. RETRIEVE: MessageStoreInterceptor 拦截器会把 "消息" 从 session 中取出来, 放入 Action 中.
③. AUTOMATIC: MessageStoreInterceptor 拦截器会把 "消息" 从 session 中取出来, 放入 Action 中. 而且若响应结果的类型的 redirect, Action 中的 "消息" 会被放入 session 中.
④. NONE: 什么都不做.
3. 使用场景: 添加新的员工后, 重定向到员工显示页面, 并显示 "员工添加成功" 的消息.
现举例说明:
1). 项目目录结构:

2). 代码:
index.jsp:
<a href="emp!list.action" mce_href="emp!list.action">List Emps</a><br><br> <a href="emp.action" mce_href="emp.action">Add Emp</a>
显示所有的员工信息: 点击: List Emps 到 EmpAction 的 list 方法.
EmpAction.java
import java.util.List; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; import com.opensymphony.xwork2.Preparable; public class EmpAction extends ActionSupport implements ModelDriven<Emp>, Preparable{ private static final long serialVersionUID = 1L; private EmpDao empDao = new EmpDao(); private List<Emp> emps = null; private Emp emp; @SuppressWarnings("static-access") public String list() throws Exception { System.out.println("list..."); emps = empDao.getAll(); return "list"; } @SuppressWarnings("static-access") public String add(){ empDao.addEmp(emp); addActionMessage("Save User Success!"); return "reload"; } @Override public Emp getModel() { return emp; } @Override public void prepare() throws Exception { } public void prepareAdd() throws Exception { emp = new Emp(); } public List<Emp> getEmps() { return emps; } public void setEmps(List<Emp> emps) { this.emps = emps; } public Emp getEmp() { return emp; } public void setEmp(Emp emp) { this.emp = emp; } } 然后转到 /WEB-INF/content/emp-list.jsp
struts.xml:
<package name="simpleit" namespace="/" extends="struts-default"> <interceptors> <interceptor-stack name="storeMessage"> <interceptor-ref name="defaultStack" /> <interceptor-ref name="store"> <param name="operationMode">AUTOMATIC</param> </interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="storeMessage" /> <action name="emp" type="redirect">/emp!list.action</result> </action> </package>
emp-list.jsp:
<s:actionmessage/> <table> <tr> <th>Name</th> <th>Email</th> </tr> <s:iterator value="emps" var="emp"> <tr> <td>${emp.name }</td> <td>${emp.email }</td> </tr> </s:iterator> </table> 添加新员工: 点击 index.jsp 页面的 Add Emp 连接, 将把页面转到 /WEB-INF/content/emp-add.jsp
emp-add.jsp:
<s:form action="emp!add.action"> <s:textfield name="name" label="Name"></s:textfield> <s:textfield name="email" label="Email"></s:textfield> <s:submit value="Submit"></s:submit> </s:form>
点击 "Submit" 按钮, 将执行 EmpAction 的 add() 方法, 然后 重定向 到 emp!list.action, 再转发到 emp-list.jsp, 在 emp-list.jsp 页面上可以通过 <s:actionmessage/> 标签显示消息.
其它代码:
Emp.java
public class Emp { private String name; private String email; public Emp() {} public Emp(String name, String email) { this.name = name; this.email = email; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } } EmpDao.java
public class EmpDao { private final static List<Emp> emps = new ArrayList<Emp>(); static{ emps.add(new Emp("Tom", "Tom@sina.com")); emps.add(new Emp("Jerry", "Jerry@sina.com")); } public static List<Emp> getAll(){ return emps; } public static void addEmp(Emp emp){ emps.add(emp); } } 4. 源码分析:
先总结下 "添加员工" 的操作流程: 提交表单, 执行 EmpAction 的 add() 方法, 在 add() 方法中通过 addActionMessage("Save User Success!"); 存放消息, 然后重定向到 list() 方法, 再在页面上通过标签显示消息. 重定向到 list() 方法后依然能够得到在 add() 方法中得到的消息, 就是 MessageStoreInterceptor 拦截器的功劳!
打开 MessageStoreInterceptor 的源代码:
public String intercept(ActionInvocation invocation) throws Exception { before(invocation); String result = invocation.invoke(); after(invocation, result); return result; } 在执行 invoke 方法之前会调用 before() 方法, 执行 invoke() 方法之后调用 after() 方法.
提交表单后, 执行 add() 方法, 然后 MessageStoreInterceptor 拦截器的 after() 方法会被调用:
protected void after(ActionInvocation invocation, String result) throws Exception { //获取 operationMode 参数 String reqOperationMode = getRequestOperationMode(invocation); //获取响应类型是否为重定向 boolean isRedirect = invocation.getResult() instanceof ServletRedirectResult; if (STORE_MODE.equalsIgnoreCase(reqOperationMode) || STORE_MODE.equalsIgnoreCase(operationMode) || (AUTOMATIC_MODE.equalsIgnoreCase(operationMode) && isRedirect)) { //获取 Action 实例 Object action = invocation.getAction(); if (action instanceof ValidationAware) { // store error / messages into session Map session = (Map) invocation.getInvocationContext().get(ActionContext.SESSION); ValidationAware validationAwareAction = (ValidationAware) action; //把 Action 实例中的 "消息" 放入 session 域中 session.put(actionErrorsSessionKey, validationAwareAction.getActionErrors()); session.put(actionMessagesSessionKey, validationAwareAction.getActionMessages()); session.put(fieldErrorsSessionKey, validationAwareAction.getFieldErrors()); } else { LOG.debug("Action ["+action+"] is not ValidationAware, no message / error that are storeable"); } } } 在执行 list() 方法之前 before() 方法会被调用:
protected void before(ActionInvocation invocation) throws Exception { String reqOperationMode = getRequestOperationMode(invocation); if (RETRIEVE_MODE.equalsIgnoreCase(reqOperationMode) || RETRIEVE_MODE.equalsIgnoreCase(operationMode) || AUTOMATIC_MODE.equalsIgnoreCase(operationMode)) { Object action = invocation.getAction(); if (action instanceof ValidationAware) { // retrieve error / message from session Map session = (Map) invocation.getInvocationContext().get(ActionContext.SESSION); ValidationAware validationAwareAction = (ValidationAware) action; LOG.debug("retrieve error / message from session to populate into action ["+action+"]"); //从 session 中获取 "消息" Collection actionErrors = (Collection) session.get(actionErrorsSessionKey); Collection actionMessages = (Collection) session.get(actionMessagesSessionKey); Map fieldErrors = (Map) session.get(fieldErrorsSessionKey); //把 "消息" 放入 Action 中 if (actionErrors != null && actionErrors.size() > 0) { Collection mergedActionErrors = mergeCollection(validationAwareAction.getActionErrors(), actionErrors); validationAwareAction.setActionErrors(mergedActionErrors); } if (actionMessages != null && actionMessages.size() > 0) { Collection mergedActionMessages = mergeCollection(validationAwareAction.getActionMessages(), actionMessages); validationAwareAction.setActionMessages(mergedActionMessages); } if (fieldErrors != null && fieldErrors.size() > 0) { Map mergedFieldErrors = mergeMap(validationAwareAction.getFieldErrors(), fieldErrors); validationAwareAction.setFieldErrors(mergedFieldErrors); } session.remove(actionErrorsSessionKey); session.remove(actionMessagesSessionKey); session.remove(fieldErrorsSessionKey); } } }