zoukankan      html  css  js  c++  java
  • Struts2入门---增删改查- 拦截器栈的应用

    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 增删改查 与拦截器的简单应用..

    文章未经版主同意不可任意转载,如有需要请标明文章出处。
  • 相关阅读:
    mysql 定时器
    mysql 存储过程记录
    mysql 常用sql
    mysql 取最后一条数据 分组
    Java8 Stream使用flatMap合并List 交 并 合 差集
    微服务技术栈
    spring boot 拦截 以及Filter和interceptor 、Aspect区别
    在springMVC的controller中获取request,response对象的一个方法
    spring boot 用@CONFIGURATIONPROPERTIES 和 @Configuration两种方法读取配置文件
    SSRS 2016 Forms Authentication
  • 原文地址:https://www.cnblogs.com/qihangzj/p/7277468.html
Copyright © 2011-2022 走看看