zoukankan      html  css  js  c++  java
  • 从源代码分析modelDriven拦截器和params拦截器和拦截器prepare 和paramsPrepareParamsStack拦截器栈(让你的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();

       }

     

    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    AGC007题解
    博弈论学习笔记
    ZROI2019 提高十连测
    男人八题2019
    LOJ 2840「JOISC 2018 Day 4」糖
    CF671D Roads in Yusland
    网络流套路小结
    BZOJ 3729 GTY的游戏
    AGC036C GP 2
    BZOJ 5046 分糖果游戏
  • 原文地址:https://www.cnblogs.com/blfshiye/p/4675863.html
Copyright © 2011-2022 走看看