码迷,mamicode.com
首页 > 其他好文 > 详细

从源代码剖析modelDriven拦截器和params拦截器和拦截器prepare 和paramsPrepareParamsStack拦截器栈(使您的Struts2代码更加简洁——怎样培养框架设计能力

时间:2014-07-19 02:18:07      阅读:247      评论:0      收藏:0      [点我收藏+]

标签:struts2   设计   拦截器   

源代码文件出处:Web App Libraries/struts2-core-2.3.15.3.jar/struts-default.xml

拦截器modelDriven

<interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>

拦截器params

<interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>

拦截器prepare

<interceptor name="prepare" class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/>

拦截器栈:

<interceptor-stack name="paramsPrepareParamsStack">

                <interceptor-ref name="exception"/>

                <interceptor-ref name="alias"/>

                <interceptor-ref name="i18n"/>

                <interceptor-ref name="checkbox"/>

                <interceptor-ref name="multiselect"/>

                <interceptor-ref name="params">

                    <param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>

                </interceptor-ref>

                <interceptor-ref name="servletConfig"/>

                <interceptor-ref name="prepare"/>

                <interceptor-ref name="chain"/>

                <interceptor-ref name="modelDriven"/>

                <interceptor-ref name="fileUpload"/>

                <interceptor-ref name="staticParams"/>

                <interceptor-ref name="actionMappingParams"/>

                <interceptor-ref name="params">

                    <param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>

                </interceptor-ref>

                <interceptor-ref name="conversionError"/>

                <interceptor-ref name="validation">

                    <param name="excludeMethods">input,back,cancel,browse</param>

                </interceptor-ref>

                <interceptor-ref name="workflow">

                    <param name="excludeMethods">input,back,cancel,browse</param>

                </interceptor-ref>

            </interceptor-stack>

 

ModelDriven拦截器的作用

当用户触发每个请求时,ModelDriven拦截器将调用JavaBean对象的getModel()方法,并把返回值类型压入到ValueStack栈

 

Params拦截器的作用:

将表单的字段映射到ValueStack栈的栈顶对象的各个属性中,因为此时ValueStack栈的栈顶元素是刚被压入的模型(JavaBean)对象(先用到ModelDriven拦截器,才有这句话),所以该模型将被填充,如果每个字段在模型里没有匹配的属性,Params拦截器将尝试ValueStack栈中的下一个对象。

 

PrepareInterceptor拦截器的作用:

u  若Action实现Preparable接口,则Action方法需实现prepare()方法

u  PrepareInterceptor拦截器将调用prepare()方法、prepareActionMethodName()方法和prepareDoActionMethodName()方法

u  PrepareInterceptor拦截器根据firstCallPrepareDo属性决定获取prepareActionMethodName、prepareDoActionNam的顺序。默认情况下先获取prepareDoActionName(),如果没有该方法,就寻找prepareDoActionMethodName()。如果找到了对应的方法就调用该方法。

u  PrepareInterceptor拦截器会根据alwaysInvokePrepare属性决定是否执行prepare()方法

 

 

paramsPrepareParamsStack拦截器栈的作用:(参考struts-default.xml配置文件的结构,就知道具体的含义了),现在详细解析一下:

u  paramsPrepareParamsStack从字面上理解来说,这里Stack的拦截器调用的顺序为:首先params,然后prepare,接下来modelDriven,最后在params

u  Struts2.0的设计上要求modelDriven在params之前调用,而业务中prepare要负责准备model,准备model又需要参数,这就需要在prepare之前运行params拦截器设置相关参数,这个也就是创建paramsPrepareParamsStack的原因。

u  流程如下:

A.       Params拦截器首先给action中的相关参数赋值,如id

B.       Prepare拦截器执行prepare方法,prepare方法中会根据参数,如id,去调用业务逻辑,设置model对象

C.       ModelDriver拦截器将model对象压入ValueStack,这里的model对象就是在prepare中创建的

D.       Params拦截器再将参数赋值给model对象

E.        Action的业务逻辑执行

 

 

请参考下面源代码解析:(第一部分是PrepareInterceptor拦截器的操作流程)

package com.opensymphony.xwork2.interceptor;

import com.opensymphony.xwork2.ActionInvocation;

import com.opensymphony.xwork2.Preparable;

import com.opensymphony.xwork2.util.logging.Logger;

import com.opensymphony.xwork2.util.logging.LoggerFactory;

 

import java.lang.reflect.InvocationTargetException;

public class PrepareInterceptor extends MethodFilterInterceptor {

 

    private static final long serialVersionUID = -5216969014510719786L;

 

    private final static String PREPARE_PREFIX = "prepare";

    private final static String ALT_PREPARE_PREFIX = "prepareDo";

 

    private boolean alwaysInvokePrepare = true;

private boolean firstCallPrepareDo = false;

 

    public void setAlwaysInvokePrepare(String alwaysInvokePrepare) {

        this.alwaysInvokePrepare = Boolean.parseBoolean(alwaysInvokePrepare);

}

 

    public void setFirstCallPrepareDo(String firstCallPrepareDo) {

        this.firstCallPrepareDo = Boolean.parseBoolean(firstCallPrepareDo);

    }

 

    @Override

    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) 方法:

package com.opensymphony.xwork2.interceptor;

import com.opensymphony.xwork2.ActionInvocation;

import com.opensymphony.xwork2.util.logging.Logger;

import com.opensymphony.xwork2.util.logging.LoggerFactory;

 

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

public class PrefixMethodInvocationUtil {

   

    private static final Logger LOG = LoggerFactory.getLogger(PrefixMethodInvocationUtil.class);

 

    private static final String DEFAULT_INVOCATION_METHODNAME = "execute";

 

private static final Class[] EMPTY_CLASS_ARRAY = new Class[0];

 

    public static void invokePrefixMethod(ActionInvocation actionInvocation, String[] prefixes) throws InvocationTargetException, IllegalAccessException {

//获取 Action 实例

       Object action = actionInvocation.getAction();

       //获取要调用的 Action 方法的名字(update)

       String methodName = actionInvocation.getProxy().getMethod();

      

       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);

//若方法不为 null, 则通过反射调用前缀方法      

if (method != null) {

           method.invoke(action, new Object[0]);

       }

    }

   

    public static Method getPrefixedMethod(String[] prefixes, String methodName, Object action) {

       assert(prefixes != null);

//把方法的首字母变为大写

        String capitalizedMethodName =capitalizeMethodName(methodName);

//遍历前缀数组

        for (String prefixe : prefixes) {

            String prefixedMethodName = prefixe + capitalizedMethodName;

//通过拼接的方式, 得到前缀方法名: 第一次 prepareUpdate, 第二次prepareDoUpdate            

try {

//利用反射获从 action 中获取对应的方法, 若有直接返回. 并结束循环.

                return action.getClass().getMethod(prefixedMethodName, EMPTY_CLASS_ARRAY);

            }

            catch (NoSuchMethodException e) {

                // hmm -- OK, try next prefix

                if (LOG.isDebugEnabled()) {

                    LOG.debug("cannot find method [#0] in action [#1]", prefixedMethodName, action.toString());

                }

            }

        }

       return null;

    }

   

    public static String capitalizeMethodName(String methodName) {

       assert(methodName != null);

       return methodName.substring(0, 1).toUpperCase() + methodName.substring(1);

    }

 

}

 

 

 

第二部分(ModelDriver拦截器的源代码解析)

 

package com.opensymphony.xwork2.interceptor;

 

import com.opensymphony.xwork2.ActionInvocation;

import com.opensymphony.xwork2.ModelDriven;

import com.opensymphony.xwork2.util.CompoundRoot;

import com.opensymphony.xwork2.util.ValueStack;

 

public class ModelDrivenInterceptor extends AbstractInterceptor {

 

    protected boolean refreshModelBeforeResult = false;

 

    public void setRefreshModelBeforeResult(boolean val) {

        this.refreshModelBeforeResult = val;

    }

 

    @Override

public String intercept(ActionInvocation invocation) throws Exception {

//获取 Action 对象: EmployeeAction 对象, 此时该 Action 已经实现了 ModelDriven 接口

    //public class EmployeeAction implements RequestAware, ModelDriven<Employee>

        Object action = invocation.getAction();

//判断 action 是否是 ModelDriven 的实例

        if (action instanceof ModelDriven) {

//强制转换为 ModelDriven 类型

            ModelDriven modelDriven = (ModelDriven) action;

//获取值栈

            ValueStack stack = invocation.getStack();

//调用 ModelDriven 接口的 getModel() 方法

            //即调用 EmployeeAction getModel() 方法

            /*

            public Employee getModel() {

              employee = new Employee();

              return employee;

           }

            */

            Object model = modelDriven.getModel();

            if (model !=  null) {

// getModel() 方法的返回值压入到值栈的栈顶. 实际压入的是 EmployeeAction employee 成员变量

              stack.push(model);

            }

            if (refreshModelBeforeResult) {

                invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model));

            }

        }

        return invocation.invoke();

    }

 

    /**

     * Refreshes the model instance on the value stack, if it has changed

     */

    protected static class RefreshModelBeforeResult implements PreResultListener {

        private Object originalModel = null;

        protected ModelDriven action;

 

 

        public RefreshModelBeforeResult(ModelDriven action, Object model) {

            this.originalModel = model;

            this.action = action;

        }

 

        public void beforeResult(ActionInvocation invocation, String resultCode) {

            ValueStack stack = invocation.getStack();

            CompoundRoot root = stack.getRoot();

 

            boolean needsRefresh = true;

            Object newModel = action.getModel();

 

            // Check to see if the new model instance is already on the stack

            for (Object item : root) {

                if (item.equals(newModel)) {

                    needsRefresh = false;

                }

            }

 

            // Add the new model on the stack

            if (needsRefresh) {

 

                // Clear off the old model instance

                if (originalModel != null) {

                    root.remove(originalModel);

                }

                if (newModel != null) {

                    stack.push(newModel);

                }

            }

        }

    }

}

 

 

细节注释:

细节一:执行 ParametersInterceptor 的 intercept 方法: 把请求参数的值赋给栈顶对象对应的属性. 若栈顶对象没有对应的属性, 则查询值栈中下一个对象对应的属性...

 

细节二:getModel 方法不能提供以下实现. 的确会返回一个 Employee 对象到值栈的栈顶. 但当前 Action 的 employee 成员变量却是 null.

@Override

   public Employee getModel() {

      return new Employee();

   }

 

从源代码剖析modelDriven拦截器和params拦截器和拦截器prepare 和paramsPrepareParamsStack拦截器栈(使您的Struts2代码更加简洁——怎样培养框架设计能力

标签:struts2   设计   拦截器   

原文地址:http://blog.csdn.net/zhongwen7710/article/details/37940143

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!