Struts2 应用拦截器 ModelDriven 和 Prepare 实现对数据库的增删改查功能.....
先看看效果图:
jsp 静态代码我就不写了 ,以下是主要实现功能的java代码:
package com.struts.app; import java.util.Map; import org.apache.struts2.interceptor.RequestAware; import com.opensymphony.xwork2.ModelDriven; import com.opensymphony.xwork2.Preparable; public class EmployeeAction implements RequestAware, ModelDriven<Employee>, Preparable{ private Dao dao = new Dao(); //实例化数据操作对象 private Employee employee; public String update(){ //定义update()方法 dao.update(employee); return "success"; } public void prepareUpdate(){ //定义 prepare 拦截器需要的方法(作用:需要用到数据对象的方法可以单一的实现) employee = new Employee(); } public String edit(){ return "edit"; } public void prepareEdit(){ employee = dao.get(employeeId); } public String save(){ dao.save(employee); return "success"; } public void prepareSave(){ employee = new Employee(); } public String delete(){ dao.delete(employeeId); //返回结果的类型应为: redirectAction. //也可以是 chain: 实际上 chain 是没有必要的 . 因为不需要在下一个 Action 中保留当前 Action 的状态 //还有, 若使用 chain , 则达到目标页面后, 地址栏显示的 依然是删除时的那个连接. 刷新时会重复提交.. return "success"; } public String list(){ request.put("emps", dao.getEmployees()); return "list"; } private Map<String, Object> request; @Override public void setRequest(Map<String, Object> request) { this.request = request; } private Integer employeeId; public void setEmployeeId(Integer employeeId) { this.employeeId = employeeId; } @Override public Employee getModel() { //这是实现 ModelDriven 重写的 getModel()方法.作用是:获取model(这样避免了在Action中定义model) return employee; } /* * prepare() 方法的主要作用: 为getModel() 方法 准备model 的.(避免了在执行方法时导致加载浪费(有些方法不需要加载对象 但它也加载了 浪费资源)) */ @Override public void prepare() throws Exception { System.out.println("prepare..."); } }
那为什么要这么写呢?这需要了解 Struts2 拦截器栈的运行流程
一般情况下我们默认的是 defaultStack 这个拦截器栈 由上图看出里面定义了许多的拦截器. 而struts 在运行的时候,都会进入这些拦截器一遍.以上每个拦截器有他对应的作用,在这里我就不一一举例了.
上面的代码就是根据 prepare拦截器 与 modelDriven拦截器 的特性所写:
下面我们来看看 modelDriven 拦截器是怎么运行的:
1. Action 类需要实现 ModelDriven接口 并重写getModel() 方法
2. ModelDriven 拦截器的运行流程:
1). 先会执行 ModelDrivenIntercepptor 的 intercept 方法.
------------------------------------源代码分析----------------------------------------------- public String intercept(ActionInvocation invocation) throws Exception { //获取 Action 对象: EmployeeAction 对象, 此时该 Action 已经实现了 ModelDriven 接口. Object action = invocation.getAction(); //判断 action 是否 是 ModelDriven 的实例 if (action instanceof ModelDriven) { //强制转换为 ModelDriven 类型 ModelDriven modelDriven = (ModelDriven) action; //获取值栈 ValueStack stack = invocation.getStack(); //调用 ModelDriven 接口的 getModel()方法 Object model = modelDriven.getModel(); if (model != null) { //把getModel()的返回值 压入到值栈的栈顶. stack.push(model); } if (refreshModelBeforeResult) { invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model)); } } return invocation.invoke(); }
2). 执行 ParametersInterceptor 的 intercept 方法:把请求参数的值赋给栈顶对象对应的属性 , 若栈顶对象没有对应的属性,
则查询值栈中下一个对象对应的属性.
3). 注意: Struts2.5版本后 通配符的写法: Action 中的方法需要被允许.
<action name="emp-*"
class="com.struts.app.EmployeeAction"
method="{1}">
<result name="{1}">/emp-{1}.jsp</result>
<result name="success" type="redirectAction">emp-list</result>
<allowed-methods>list,save,edit,update,delete</allowed-methods>
</action>
同理 (prepare) PrepareInterceptor拦截器运行流程:
---------------------------------源代码解析-------------------------------------- public String doIntercept(ActionInvocation invocation) throws Exception { //获取 Action 实例 Object action = invocation.getAction(); //判断 Action 是否实现了 Preparable 接口 if (action instanceof Preparable) { try { String[] prefixes; //根据当前拦截器的 firstCallPrepareDo(默认为false) 属性确定 prefixes if (firstCallPrepareDo) { prefixes = new String[] {ALT_PREPARE_PREFIX, PREPARE_PREFIX}; } else { prefixes = new String[] {PREPARE_PREFIX, ALT_PREPARE_PREFIX}; } // 若为 false,则 prefixes: prepare, prepareDo //调用前缀方法 PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof Exception) { throw (Exception) cause; } else if(cause instanceof Error) { throw (Error) cause; } else { throw e; } } //根据当前的 alwaysInvokePrepare(默认true) 决定是否 调用 Action 的 prepare 方法 if (alwaysInvokePrepare) { ((Preparable) action).prepare(); } } return invocation.invoke(); } PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes) 方法: public static void invokePrefixMethod(ActionInvocation actionInvocation, String[] prefixes)
throws InvocationTargetException, IllegalAccessException { //获取 Action 实例 Object action = actionInvocation.getAction(); //获取调用的 Action 中的 方法名(update) String methodName = actionInvocation.getProxy().getMethod(); //如果方法名为空 默认为 execute if (methodName == null) { // if null returns (possible according to the docs), use the default execute methodName = DEFAULT_INVOCATION_METHODNAME; } //若方法名不为空, 则调用前缀方法 Method method = getPrefixedMethod(prefixes, methodName, action); if (method != null) { method.invoke(action, new Object[0]); } } PrefixMethodInvocationUtil.getPrefixedMethod 方法: public static Method getPrefixedMethod(String[] prefixes, String methodName, Object action) { assert(prefixes != null); //将方法名首字母置为大写(Update) String capitalizedMethodName = capitalizeMethodName(methodName); //遍历前缀数组 for (String prefixe : prefixes) { //通过拼接的方式得到 前缀方法名 : 第一次prepareUpdate,第二次prepareDoUpdate String prefixedMethodName = prefixe + capitalizedMethodName; try { //利用反射从 action 中 获取对应的方法, 若有直接返回. 并结束循环. return action.getClass().getMethod(prefixedMethodName, EMPTY_CLASS_ARRAY); } catch (NoSuchMethodException e) { // hmm -- OK, try next prefix LOG.debug("Cannot find method [{}] in action [{}]", prefixedMethodName, action); } } return null; }
解析源代码得出结论:
若 Action 实现了 Preparable 接口 则 Struts2 将尝试执行 prepare[ActionMethodName] 方法,
若 prepare[ActionMethodName] 不存在,则将尝试执行 prepareDo[ActionMethodName] 方法.
若都不存在,就都不执行.
若 PrepareInterceptor 的 alwaysInvokePrepare 属性为 false,
则Struts2 将不会调用该实现了 Preparable 的 Action 的 prepare() 方法.
可以为每一个 ActionMethod 准备 prepare[ActionMethodName] 方法, 而抛弃掉原来的 prepare() 方法
将 PrepareInterceptor 的 alwaysInvokePrepare 属性设置为 false, 避免 Struts2 框架再调用 prepare() 方法.
如何在配置文件中为拦截器栈的属性赋值:参看 /struts-2.5.12/docs/docs/interceptors.html
<interceptors> <interceptor-stack name="myStack"> <interceptor-ref name="paramsPrepareParamsStack"> <param name="prepare.alwaysInvokePrepare">false</param> </interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="myStack"/>
这个项目中我们需要用struts 的 paramsPrepareParamsStack 这个栈.
使用 paramsPrepareParamsStack 拦截器栈后的运行流程
1). paramsPrepareParamsStack 和 defaultStack 一样都是拦截器栈, 而 struts-default 包默认使用的是 defaultStack
2). 可以在struts 配置文件中通过以下方式修改默认的拦截器栈:
<default-interceptor-ref name="paramsPrepareParamsStack"></default-interceptor-ref>
3). paramsPrepareParamsStack 拦截器栈 特殊之处:
params ->ModelDriven -> params
这样可以先把请求参数赋值给 Action 对应的属性, 再根据赋给 Action 的那个属性值决定 压入到值栈栈顶的对象, 最后再为栈顶对象的属性赋值.(用于表单发送请求参数不同给出不同的响应结果)
4). 关于回显: Struts2 表单标签会从值栈中获取对应的属性值进行回显.
以上就是关于struts 增删改查 与拦截器的简单应用..