Java 杂谈

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

2019-08-04  本文已影响5人  一颗白菜_

ModelDriven拦截器

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

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();
    }
public Employee getModel(){
    return new Employee();
}

paramsPrepareParamsStack拦截器栈

小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

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()方法。

<?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 {
        //该方法以及不会被调用了
    }
}
上一篇下一篇

猜你喜欢

热点阅读