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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[原创]java WEB学习笔记6:Struts2 学习之路--Struts的CRUD操作( 查看 / 删除/ 添加) 使用 paramsPrepareParamsStack 重构代码 ,Prepar相关的知识,希望对你有一定的参考价值。

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

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

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

微博:伊直都在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 ,prepareDo
15                 //调用前缀方法
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 apitalizedMethodName
67             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 prefix
74                 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-list
43         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     @Override
68     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     @Override
79     public EmployeeBean getModel() {
80 
81         return employeeBean;
82     }
83 
84     /*
85      * prepare 方法的主要作用:为getModel() 方法准备 model 的
86      */
87     @Override
88     public void prepare() throws Exception {
89         System.out.println("prepare ... ");
90     }
91 
92 }

 

以上是关于[原创]java WEB学习笔记6:Struts2 学习之路--Struts的CRUD操作( 查看 / 删除/ 添加) 使用 paramsPrepareParamsStack 重构代码 ,Prepar的主要内容,如果未能解决你的问题,请参考以下文章

[原创]java WEB学习笔记70:Struts2 学习之路-- struts2拦截器源码分析,运行流程

[原创]java WEB学习笔记75:Struts2 学习之路-- 总结 和 目录

[原创]java WEB学习笔记70:Struts2 学习之路-- 输入验证,声明式验证,声明是验证原理

[原创]java WEB学习笔记73:Struts2 学习之路-- strut2中防止表单重复提交

[原创]java WEB学习笔记58:Struts2学习之路---Result 详解 type属性,通配符映射

[原创]java WEB学习笔记72:Struts2 学习之路-- 文件的上传下载,及上传下载相关问题