美文网首页Java 杂谈
Struts2学习笔记 | ModelDriven拦截器和par

Struts2学习笔记 | ModelDriven拦截器和par

作者: 一颗白菜_ | 来源:发表于2019-08-04 22:59 被阅读5次

    ModelDriven拦截器

    • 首先要认识到,把Action和Model分隔开是有必要的。有些Action类不代表任何Model对象,他们的功能仅限于提供显示服务。

    • 如果Action实现了ModelDriven接口,该拦截器将把ModelDriven接口的getModel()方法返回的对象置于栈顶。

    实现了ModelDriven后的Action类运行流程(Struts2源码)

    • 先执行ModelDrivenInterceptorintercept方法
    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()方法
                //即调用EmployeeAction的getModel()方法
                Object model = modelDriven.getModel();
                if (model != null) {
                    //把getModel()方法的返回值压入到值栈的栈顶,实际压入的是EmployeeAction的Employee的成员变量
                    stack.push(model);
                }
    
                if (this.refreshModelBeforeResult) {
                    invocation.addPreResultListener(new ModelDrivenInterceptor.RefreshModelBeforeResult(modelDriven, model));
                }
            }
    
            return invocation.invoke();
        }
    
    • 然后执行ParametersInterceptorintercept方法,该方法会把请求参数的值赋给栈顶对象对应的属性,若栈顶对象没有对应的属性,则查询值栈中下一个对象对应的属性。

    • 注意
      getModel方法不能返回匿名对象,虽然返回一个匿名类也可以将其添加到栈顶,但是当前Action类的employee成员变量却是null。也就是说成员变量的employee和返回的匿名对象不是同一个对象。

    public Employee getModel(){
        return new Employee();
    }
    

    paramsPrepareParamsStack拦截器栈

    • paramsPrepareParamsStackparamsPrepareParamsStack一样都是拦截器栈。而struts-default包默认使用paramsPrepareParamsStack拦截器栈。

    • 若要使用paramsPrepareParamsStack,则需要在struts.xml文件中配置使用paramsPrepareParams作为默认的拦截器栈,在struts.xml文件的package节点内配置<default-interceptor-ref name="paramsPrepareParamsStack"></default-interceptor-ref>,配置后则是使用paramsPrepareParamsStack作为默认的拦截器栈。

    • paramsPrepareParamsStack拦截器栈中的拦截器的调用顺序是先运行params,再运行modelDriven,最后再次运行params因此可以先把请求参数赋给Action对应的属性,再根据 赋给Action的那个属性值 决定压到值栈栈顶的对象,最后再为栈顶对象的属性赋值

    小Demo:进行edit操作,即表单的编辑(更新)操作
    Action类的代码:

    package struts.crud;
    
    import com.opensymphony.xwork2.ModelDriven;
    import org.apache.struts2.interceptor.RequestAware;
    
    import java.util.Map;
    /**
     * 该代码存在的问题:
     * 1.在删除的时候,employeeId肯定不为null,但getModel方法却从数据库中加载了一个对象(多余的步骤)
     * 2.执行查询全部信息时,也创建了个Employee对象(浪费)
     * 
     */
    public class EmployeeAction implements RequestAware, ModelDriven<Employee> {
        private Dao dao = new Dao();
        private Employee employee;
        private Map<String,Object> requests;
        //需要在当前的EmployeeAction中定义employeeId属性,用来接收请求参数
        private Integer employeeId;
    
        public void setEmployeeId(Integer employeeId) {
            this.employeeId = employeeId;
        }
    
        /**
         * 在此代码中:
         * 先为EmployeeAction的employeeId赋值(在jsp页面已经传过来了,此处忽略)
         * 再根据employeeId从数据库中加载对应的对象放入到值栈中
         * 再为栈顶对象的employeeId赋值(实际上此时employeeId已经存在)
         * 再把栈顶对象的属性回显到表单中
         * @return
         */
        public String edit(){
            return "edit";
        }
        public String delete(){
            dao.delete(employeeId);
            return "success";
        }
    
        public String save(){
            dao.save(employee);
            return "success";
        }
        public String update(){
            dao.update(employee);
            return "success";
        }
        public String list(){
            requests.put("emps",dao.getEmployees());
            return "list";
        }
    
        @Override
        public void setRequest(Map < String, Object > map) {
            requests = map;
        }
    
        @Override
        public Employee getModel() {
            //employeeId为空,则是新建操作,则要重新new一个Employee对象并返回
            if(employeeId == null)
                employee = new Employee();
            else//若不为空,则是更新操作,则直接从数据库中拿到该对象并返回即可
                employee = dao.get(employeeId);
    
            return employee;
        }
    }
    
    

    关于表单回显

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


    perpareInterceptor拦截器

    上面我们提到的modelDriven拦截器是负责把Action类以外的一个对象压入到值栈栈顶,而prepare拦截器负责准备为getModel()方法准备model

    • 若Action实现了Preparable接口,则Struts2将尝试执行prepare[ActionMethodName]方法,若prepare[ActionMethodName]方法不存在,则尝试执行prepareDo[ActionMethodName]方法,若都不存在,就都不执行。

    • alwaysInvokePrepare属性为false,则Struts2将不会调用实现了Preparable接口的Action的prepare()方法

    • 源码解析

    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 (this.firstCallPrepareDo) {
                        prefixes = new String[]{"prepareDo", "prepare"};
                    } else {
                        prefixes = new String[]{"prepare", "prepareDo"};
                    }
                    //若为false,则prefixes:"prepare", "prepareDo"
                    //调用前缀方法
                    PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes);
                } catch (InvocationTargetException var5) {
                    Throwable cause = var5.getCause();
                    if (cause instanceof Exception) {
                        throw (Exception)cause;
                    }
    
                    if (cause instanceof Error) {
                        throw (Error)cause;
                    }
    
                    throw var5;
                }
                //根据当前拦截器的alwaysInvokePrepare(默认是true)决定是否调用Action的prepare方法
                if (this.alwaysInvokePrepare) {
                    ((Preparable)action).prepare();
                }
            }
    
            return invocation.invoke();
        }
    
    public static void invokePrefixMethod(ActionInvocation actionInvocation, String[] prefixes) throws InvocationTargetException, IllegalAccessException {
            //获取Action实例
            Object action = actionInvocation.getAction();
            //获取要调用的Action方法的名字,
            String methodName = actionInvocation.getProxy().getMethod();
            if (methodName == null) {
                methodName = "execute";
            }
            //获取前缀方法
            Method method = getPrefixedMethod(prefixes, methodName, action);
            //若方法不为null,则通过反射调用前缀方法
            if (method != null) {
                method.invoke(action);
            }
    
        }
    
    public static Method getPrefixedMethod(String[] prefixes, String methodName, Object action) {
            assert prefixes != null;
            //把方法名的首字母变为大写
            String capitalizedMethodName = capitalizeMethodName(methodName);
            String[] arr$ = prefixes;
            int len$ = prefixes.length;
            int i$ = 0;
            //遍历前缀数组
            while(i$ < len$) {
                String prefixe = arr$[i$];
                //通过拼接的方式,得到前缀方法名,第一次为prepare+[方法名],第二次为prepareDo+[方法名]
                String prefixedMethodName = prefixe + capitalizedMethodName;
    
                try {
                    //利用反射从action中获取对应的方法,若有则返回,并结束循环。
                    return action.getClass().getMethod(prefixedMethodName, EMPTY_CLASS_ARRAY);
                } catch (NoSuchMethodException var10) {
                    LOG.debug("Cannot find method [{}] in action [{}]", prefixedMethodName, action);
                    ++i$;
                }
            }
    
            return null;
        }
    

    所以现在,对于之前paramsPrepareParamsStack拦截器栈的Demo中存在的问题。可以这么解决:
    可以为每一个ActionMethod准备prepare[ActionMethodName]方法,而抛弃原来的prepare()方法,将PrepareInterceptoralwaysInvokePrepare属性置为false,以避免Struts2框架再调用prepare()方法。

    • 在配置文件中为拦截器栈的属性赋值
      struts.xml文件中使用如下来设置:
    <?xml version="1.0" encoding="UTF-8"?>
    
    <!DOCTYPE struts PUBLIC
            "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
            "http://struts.apache.org/dtds/struts-2.5.dtd">
    
    <struts>
        <package name="default" extends="struts-default" strict-method-invocation="false">
            <!-- 修改PrepareInterceptor拦截器的alwaysInvokePrepare属性为false-->
            <interceptors>
                <!-- 名字自取-->
                <interceptor-stack name="cerrStack">
                    <!-- 指向的拦截器栈 paramsPrepareParamsStack-->
                    <interceptor-ref name="paramsPrepareParamsStack">
                        <param name="prepare.alwaysInvokePrepare">false</param>
                    </interceptor-ref>
                </interceptor-stack>
            </interceptors>
            <!-- 配置使用cerrStack(自己配置的)作为默认的拦截器栈-->
            <default-interceptor-ref name="cerrStack"></default-interceptor-ref>
        </package>
    </struts>
    

    重新编写之后的Action类文件如下:

    package struts.crud;
    
    import com.opensymphony.xwork2.ModelDriven;
    import com.opensymphony.xwork2.Preparable;
    import org.apache.struts2.interceptor.RequestAware;
    
    import java.util.Map;
    
    public class EmployeeAction implements RequestAware, ModelDriven<Employee>, Preparable {
        private Dao dao = new Dao();
        private Employee employee;
        private Map<String,Object> requests;
        //需要在当前的EmployeeAction中定义employeeId属性,用来接收请求参数
        private Integer employeeId;
    
        public void setEmployeeId(Integer employeeId) {
            this.employeeId = employeeId;
        }
    
        /**
         * 为edit做准备
         */
        public void prepareEdit(){
            employee = dao.get(employeeId);
        }
    
        public String edit(){
            return "edit";
        }
        public String delete(){
            dao.delete(employeeId);
            return "success";
        }
    
        /**
         * 为save()做准备
         */
        public void prepareSave(){
            employee = new Employee();
        }
    
        public String save(){
            dao.save(employee);
            return "success";
        }
    
        public void prepareUpdate(){
            employee = new Employee();
        }
        public String update(){
            dao.update(employee);
            return "success";
        }
        public String list(){
            requests.put("emps",dao.getEmployees());
            return "list";
        }
    
        @Override
        public void setRequest(Map < String, Object > map) {
            requests = map;
        }
    
        @Override
        public Employee getModel() {
            return employee;
        }
    
        /**
         * 作用:为getModel()方法准备model的
         * @throws Exception
         */
        @Override
        public void prepare() throws Exception {
            //该方法以及不会被调用了
        }
    }
    

    相关文章

      网友评论

        本文标题:Struts2学习笔记 | ModelDriven拦截器和par

        本文链接:https://www.haomeiwen.com/subject/gfrydctx.html