首页 > 代码库 > [原创]java WEB学习笔记6:Struts2 学习之路--Struts的CRUD操作( 查看 / 删除/ 添加) 使用 paramsPrepareParamsStack 重构代码 ,PrepareInterceptor拦截器,paramsPrepareParamsStack 拦截器栈

[原创]java WEB学习笔记6:Struts2 学习之路--Struts的CRUD操作( 查看 / 删除/ 添加) 使用 paramsPrepareParamsStack 重构代码 ,PrepareInterceptor拦截器,paramsPrepareParamsStack 拦截器栈

本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用

内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系。

本人互联网技术爱好者,互联网技术发烧友

微博:伊直都在0221

QQ:951226918

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

使用   paramsPrepareParamsStack 拦截器的运行流程

   1)paramsPrepareParamsStack  和 defaultSstack 一样都是拦截器栈,而struts-defalt包默认使用的是defaultStack

   2)可以通过在struts.xml 中配置默认的拦截器栈 

              <!-- 配置使用paramsPrepareParamsStack 作为默认的拦截器栈 -->

         <default-interceptor-ref name="paramsPrepareParamsStack"></default-interceptor-ref>

 

  3)paramsPrepareParamsStack   拦截器在的运行过程: params -> modelDriven -> params

    可以先把请求参数赋给 Action 对应的属性,再根据赋给Action 的属性值 决定压入值栈栈顶的对象,最后再为栈顶对象的属性赋值

  技术分享

  4)使用 paramsPrepareParamsStack 拦截器栈:

    Struts 2.0的设计上要求 modelDriven 在 params 之前调用,而业务中prepare要负责准备model,准备model又需要参数,

    这就需要在 prepare之前运行params拦截器设置相关参数,这个也就是创建paramsPrepareParamsStack的原因。

  技术分享

  

 

 

1.修改默认的拦截器在struts.xml 中

  

 1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE struts PUBLIC 3     "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" 4     "http://struts.apache.org/dtds/struts-2.3.dtd"> 5  6 <struts> 7  8      <constant name="struts.action.extension" value="action,do,"></constant> 9      <package name="default" namespace="/" extends="struts-default">10          11          <!-- 配置使用paramsPrepareParamsStack 作为默认的拦截器栈 -->12          <default-interceptor-ref name="paramsPrepareParamsStack"></default-interceptor-ref>13          <action name="emp-*" class="com.jason.strut2.curd.EmployeeAction" method="{1}">14              <result name="{1}">/emp-{1}.jsp</result>15              <result name="success" type="redirectAction">emp-list</result>16          </action>17          18          19      </package>20 </struts>

 

 

2.重构 acntion代码

 

  对于edit 操作而言

    1)先为 EmployeeAction 的 employeeId 赋值

    2)再根据employeeId 从数据库中加载对应的对象,并且压入到值栈的栈顶

    3)再为栈顶对象的employeeId 赋值

    4)把栈顶对象的属性回显在表单中

 

  关于回显:

    struts2 表单标签会从值栈中获取对应的属性值进行回显

 

  存在问题:

      

      public EmployeeBean getModel() {
          if(employeeId == null){
             employeeBean = new EmployeeBean();
          } else {
             employeeBean = dao.get(employeeId);
          }
          return employeeBean;
      }

 

    1)在执行删除的时候,employeeId 不为null,但getModel 方法却从数据库中加载了一个对象,多余

    2)在查询全部的时候,new Empployee() 对象,多余

 解决问题方案:

    使用 PrepareIntercepter 和 Preparable 接口

  

  1 package com.jason.strut2.curd;  2   3 import java.util.Map;  4   5 import org.apache.struts2.interceptor.RequestAware;  6   7 import com.opensymphony.xwork2.ActionContext;  8 import com.opensymphony.xwork2.ActionInvocation;  9 import com.opensymphony.xwork2.ModelDriven; 10 import com.opensymphony.xwork2.util.ValueStack; 11  12 public class EmployeeAction implements RequestAware ,ModelDriven<EmployeeBean>{ 13  14     private Dao dao = new Dao(); 15     private Map<String, Object> requestMap; 16     private EmployeeBean employeeBean; 17  18     // 需要在当前的 EmployeeAction 中定义employeeId 属性,以接收页面请求参数:删除操作 19      20  21     public String list() { 22  23         requestMap.put("emps", dao.getEmployees()); 24         return "list"; 25     } 26  27     //删除 28     public String delete() { 29  30         dao.delete(employeeId); 31         // 返回结果的类型应为 :redirectAction,也可以是chain:实际chain 是没有必要 32         // 还有,若使用chain 则到达目标页面后,地址栏显示的依然是 删除 的那个连接,刷新时 会有重复提交 33         return "success"; 34     } 35  36      37     public String save(){ 38          39          40          41             //1.获取请求参数:通过定义属性的方式 42             //2.调用DAO的 svae 方法 43      44             dao.save(employeeBean); 45             //3.通过redirectAction 的方式响应结果给 emp-list 46             return "success"; 47     } 48      49      50     public String update(){ 51         dao.update(employeeBean); 52         return "success"; 53     } 54      55     public String edit(){ 56          57          //1.获取传入的employeeId:employee.getEmployeeId() 58          //2.根据employeesId  获取employee对象 59          //EmployeeBean empl = dao.get(employeeBean.getEmployeeId()); 60           61          //3.把栈顶的对象的属性装配好:此时的栈顶对象是employee  62          //目前的employee 对象只有 employeeId 属性,其他属性为 null 63           64          /* 65           *Struts2表单回显:从值栈栈顶开始查找匹配的属性,若找到就添加到value 属性中。  66           */ 67         /*employeeBean.setEmail(empl.getEmail()); 68         employeeBean.setFirstNmame(empl.getFirstName()); 69         employeeBean.setLastName(empl.getLastName());*/ 70          71         //不能进行表单的回显,因为经过重写赋值的employee 对象已经不再是栈顶对象 72         // employeeBean  = dao.get(employeeBean.getEmployeeId()); 73          74         //手动的把从该数据库中获取的Employee 对象放到值栈的栈顶 75         //但是此时值栈栈顶及第二个对象均为employee 对象 76         //ActionContext.getContext().getValueStack().push(dao.get(employeeBean.getEmployeeId())); 77           78          79         return "edit"; 80     } 81      82     @Override 83     public void setRequest(Map<String, Object> requestMap) { 84         this.requestMap = requestMap; 85     } 86  87     private Integer employeeId; 88     public void setEmployeeId(Integer employeeId) { 89         this.employeeId = employeeId; 90     } 91      92     @Override 93     public EmployeeBean getModel() { 94         //判断Create  还是 edit 95         //若为Create,则:employeeBean  = new EmployeeBean(); 96         //若为edit ,则从数据库中获取 employeeBean  = dao.get(employeeBean.getEmployeeId()); 97         //判断标准为:是否有employeeId。若有则视为 edit,若没有则视为 Create 98         //若通过employeeId 来判断,则需要在modelDriven 拦截器之前先执行一个params拦截器 99         //可以通过使用paramsPrepareParams 拦截器实现100         //需要在 struts.xml 文件中配置 paramsPrepareParams 为默认的拦截器栈101         102         if(employeeId == null){103             employeeBean  = new EmployeeBean();            104         } else { 105             employeeBean = dao.get(employeeId);106         }107         return employeeBean;108     }109 110 }

 

 

3.其他 代码 
EmployeeBean Dao

 web.xml  emp-list.jsp  emp-edit.jsp inde.jsp 参考

[原创]java WEB学习笔记65:Struts2 学习之路--Struts的CRUD操作( 查看 / 删除/ 添加) ModelDriven拦截器 paramter 拦截器

 

4. Preparable 拦截器

   1)Struts 2.0 中的 modelDriven 拦截器负责把 Action 类以外的一个对象压入到值栈栈顶

   2)而 prepare 拦截器负责准备为 getModel() 方法准备 model

 

    3)关于PrepareIntercepter 源码结论

    ① 若Action 实现了Preparable 接口,则 Struts 将尝试执行prepare[ActionMethodName]方法;

        若prepare[ActionMethodName] 不存在,则将尝试执行prepareDo[ActionMethodName] 方法;

            若都不存在,就都不执行; 

    ② 若PrepareIntercepter  的  alwaysInvokePrepare 属性为false,则struts2 将不会调用实现了Preparable 接口的 Action 的prepare 方法

    4)PrepareInterceptor 拦截器用方法

    ①若 Action 实现 Preparable 接口,则 Action 方法需实现 prepare() 方法

               ② PrepareInterceptor 拦截器将调用 prepare() 方法  prepareActionMethodName()方法 或 prepareDoActionMethodName ()方法   

               ③PrepareInterceptor 拦截器根据 firstCallPrepareDo 属性决定获取 prepareActionMethodName 、prepareDoActionMethodName的顺序。默认情况下先获取 prepareActionMethodName (), 如果没有该方法,就寻找prepareDoActionMethodName()。如果找到对应的方法就调用          该方法

               ④PrepareInterceptor 拦截器会根据 alwaysInvokePrepare 属性决定是否执行prepare()方法

   5)源码分析 

 1 @Override 2     public String doIntercept(ActionInvocation invocation) throws Exception { 3         Object action = invocation.getAction(); 4  5         if (action instanceof Preparable) { 6             try { 7                 String[] prefixes; 8                 //根据当前拦截器的 firstCallPrepareDo 确定 前缀数组;firstCallPrepareDo 默认为false 9                 if (firstCallPrepareDo) {10                     prefixes = new String[] {ALT_PREPARE_PREFIX, PREPARE_PREFIX};11                 } else {12                     prefixes = new String[] {PREPARE_PREFIX, ALT_PREPARE_PREFIX};13                 }14                 //若为false: 则prefixex 为 prepare ,prepareDo15                 //调用前缀方法16                 PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes);17             }18             catch (InvocationTargetException e) {19                 20                 Throwable cause = e.getCause();21                 if (cause instanceof Exception) {22                     throw (Exception) cause;23                 } else if(cause instanceof Error) {24                     throw (Error) cause;25                 } else {26                     27                     throw e;28                 }29             }30 31             //根据当前拦截器的 alwaysInvokePrepare(默认是true) 决定是否调用 Action 的 prepare 方法32             if (alwaysInvokePrepare) {33                 ((Preparable) action).prepare();34             }35         }36 37         return invocation.invoke();38     }39     40 41     42     public static void invokePrefixMethod(ActionInvocation actionInvocation, String[] prefixes) throws InvocationTargetException, IllegalAccessException {43         //过去Action 实例44         Object action = actionInvocation.getAction();45         //获取要调用的Action 方法的名字(update)46         String methodName = actionInvocation.getProxy().getMethod();47         48         if (methodName == null) {49             // if null returns (possible according to the docs), use the default execute 50             methodName = DEFAULT_INVOCATION_METHODNAME;51         }52         //获取前缀方法53         Method method = getPrefixedMethod(prefixes, methodName, action);54         if (method != null) {55             //若方法不为 null,通过反射调用前缀方法56             method.invoke(action, new Object[0]);57         }58     }59     60     public static Method getPrefixedMethod(String[] prefixes, String methodName, Object action) {61         assert(prefixes != null);62         //把方法的名字变为大写63         String capitalizedMethodName = capitalizeMethodName(methodName);64         //遍历前缀数组65         for (String prefixe : prefixes) {66             //通过拼接的方式,得到前缀方法名:第一次 prepare + capitalizedMethodName ;第二次 prepareDo +c apitalizedMethodName67             String prefixedMethodName = prefixe + capitalizedMethodName;68             try {69                 //利用反射从aciton 中获取对应的方法,若有,直接返回并结束循环70                 return action.getClass().getMethod(prefixedMethodName, EMPTY_CLASS_ARRAY);71             }72             catch (NoSuchMethodException e) {73                 // hmm -- OK, try next prefix74                 if (LOG.isDebugEnabled()) {75                     LOG.debug("cannot find method [#0] in action [#1]", prefixedMethodName, action.toString());76                 }77             }78         }79         return null;80     }

 

 

   6)解决上述上述问题的方法:

    方案:为每一个ActionMethod 准备一个prepare[ActionMethodName] 方法,而抛弃原来的prepare()方法;将PrepareIntercepter  的  alwaysInvokePrepare 属性设为false,避免Struts2 框架在嗲用prepare()方法

 

 1 package com.jason.strut2.curd; 2  3 import java.util.Map; 4  5 import org.apache.struts2.interceptor.RequestAware; 6  7 import com.opensymphony.xwork2.ActionContext; 8 import com.opensymphony.xwork2.ActionInvocation; 9 import com.opensymphony.xwork2.ModelDriven;10 import com.opensymphony.xwork2.Preparable;11 import com.opensymphony.xwork2.util.ValueStack;12 13 public class EmployeeAction implements RequestAware, ModelDriven<EmployeeBean>,14         Preparable {15 16     private Dao dao = new Dao();17     private Map<String, Object> requestMap;18     private EmployeeBean employeeBean;19 20     // 需要在当前的 EmployeeAction 中定义employeeId 属性,以接收页面请求参数:删除操作21 22     public String list() {23 24         requestMap.put("emps", dao.getEmployees());25         return "list";26     }27 28     // 删除29     public String delete() {30 31         dao.delete(employeeId);32         // 返回结果的类型应为 :redirectAction,也可以是chain:实际chain 是没有必要33         // 还有,若使用chain 则到达目标页面后,地址栏显示的依然是 删除 的那个连接,刷新时 会有重复提交34         return "success";35     }36 37     public String save() {38         // 1.获取请求参数:通过定义属性的方式39         // 2.调用DAO的 svae 方法40 41         dao.save(employeeBean);42         // 3.通过redirectAction 的方式响应结果给 emp-list43         return "success";44     }45 46     public void prepareSave() {47         employeeBean = new EmployeeBean();48     }49 50     public void prepareUpdate() {51         employeeBean = new EmployeeBean();52     }53 54     public String update() {55         dao.update(employeeBean);56         return "success";57     }58 59     public void prepareEidt() {60         employeeBean = dao.get(employeeId);61     }62 63     public String edit() {64         return "edit";65     }66 67     @Override68     public void setRequest(Map<String, Object> requestMap) {69         this.requestMap = requestMap;70     }71 72     private Integer employeeId;73 74     public void setEmployeeId(Integer employeeId) {75         this.employeeId = employeeId;76     }77 78     @Override79     public EmployeeBean getModel() {80 81         return employeeBean;82     }83 84     /*85      * prepare 方法的主要作用:为getModel() 方法准备 model 的86      */87     @Override88     public void prepare() throws Exception {89         System.out.println("prepare ... ");90     }91 92 }

 

[原创]java WEB学习笔记6:Struts2 学习之路--Struts的CRUD操作( 查看 / 删除/ 添加) 使用 paramsPrepareParamsStack 重构代码 ,PrepareInterceptor拦截器,paramsPrepareParamsStack 拦截器栈