zoukankan      html  css  js  c++  java
  • [原创]java WEB学习笔记66: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 ,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类练习一则
    windows下安装Apache+PHP
    Java 数组/对象练习一则
    windows 安装 apache 报错解决
    延时任务机制
    Javashop电商系统-会员登录方式
    基于canvas商品海报生成源码分享
    uniapp引入微信小程序直播组件
    电商系统中库存的存储于扣减
    电商系统nuxt的中间件代码分享
  • 原文地址:https://www.cnblogs.com/jasonHome/p/5913927.html
Copyright © 2011-2022 走看看