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

ModelDriven 和 Preparable 拦截器

时间:2015-04-07 15:24:37      阅读:398      评论:0      收藏:0      [点我收藏+]

标签:

  • 首先了解Params 拦截器
作用:Parameters 拦截器将把表单字段映射到 ValueStack 栈的栈顶对象的各个属性中. 如果某个字段在模型里没有匹配的属性, Param 拦截器将尝试 ValueStack 栈中的下一个对象
技术分享
把表单的值赋给栈顶对象的属性   此时栈顶对象即为 Action

在实际的使用中我们想  把 Action 和 Model 清晰地隔离开是有必要的: 有些 Action 类不代表任何Model 对象, 它们的功能仅限于提供显示服务
问题:所以我要在Params 拦截器之前需要把一个Model压入栈顶,以便于在执行到Params 拦截器的时候栈顶对象是个Model 从而对Action中的表单域中对象model属性进行赋值。
这就需要我们使用ModelDriven拦截器
 
  • ModelDriven 拦截器
如果 Action 类实现了 ModelDriven 接口,该拦截器将把 ModelDriven 接口的 getModel() 方法返回的对象置于栈顶
 
public class EmployeeAction extends ActionSupport implements ModelDriven<Employee>{
 
     private EmployeeService employeeService;
 
     public void setEmployeeService(EmployeeService employeeService) {
           this.employeeService = employeeService;
     } 
     public String save() {
           
           employeeService.save(employee );
           return "save-success" ;
     } 
     private Employee employee;
 
//该拦截器将把 ModelDriven 接口的 getModel() 方法返回的对象置于栈顶
     @Override
     public Employee getModel() {
           return employee ;
     } 
}
当用户触发 save请求时, ModelDriven 拦截器将调用 EmployeeAction 对象的 getModel() 方法, 并把返回的模型(Employee实例)压入到 ValueStack 栈. 
接下来 Parameters 拦截器将把表单字段映射到 ValueStack 栈的栈顶对象的各个属性中. 因为此时 ValueStack 栈的栈顶元素是刚被压入的模型(Employee)对象, 所以该模型将被填充. 如果某个字段在模型里没有匹配的属性, Param 拦截器将尝试 ValueStack 栈中的下一个对象
 
查看ModelDriven 底层源代码:
核心代码:
public class ModelDrivenInterceptor extends AbstractInterceptor {
 
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        Object action = invocation.getAction();
          //判断action是否实现 ModelDriven的接口
        if (action instanceof ModelDriven) {
          
            ModelDriven modelDriven = (ModelDriven) action;
            //获取值栈
            ValueStack stack = invocation.getStack();
          //在这里调用了action中实现的MOdelDriven方法 返回一个对象
            Object model = modelDriven.getModel();
            if (model !=  null) {
               //把返回的对象压入栈顶
              stack.push(model);
            }
            if (refreshModelBeforeResult ) {
                invocation.addPreResultListener( new RefreshModelBeforeResult(modelDriven, model));
            }
        }
        return invocation.invoke();
    }
}
 
 
注意:表单元素的name属性要和对应的model的属性名一至
 
 
  • Preparable 拦截器 
Struts 2.0 中的 modelDriven 拦截器负责把 Action 类以外的一个对象压入到值栈栈顶
而 prepare 拦截器负责准备为 getModel() 方法准备 model  但
Preparable 拦截器没有在struts-default中这就需要我们使用 paramsPrepareParamsStack 拦截器栈
 
使用 paramsPrepareParamsStack 拦截器栈
 
paramsPrepareParamsStack 从字面上理解来说, 这个stack的拦截器调用的顺序为:首先 params,然后 prepare,接下来 modelDriven,最后再 params
Struts 2.0的设计上要求 modelDriven 在 params 之前调用,而业务中prepare要负责准备model,准备model又需要参数,这就需要在 prepare之前运行params拦截器设置相关参数,这个也就是创建paramsPrepareParamsStack的原因。
 
流程如下:
1. params拦截器首先给action中的相关参数赋值,如id
2. prepare拦截器执行prepare方法,prepare方法中会根据参数,如id,去调用业务逻辑,设置model对象
3. modelDriven拦截器将model对象压入value stack,这里的model对象就是在prepare中创建的
4. params拦截器再将参数赋值给model对象
5. action的业务逻辑执行
 
技术分享
实例代码:

public class EmployeeAction extends ActionSupport implements Preparable,ModelDriven<Employee>{
      1. params拦截器首先给action中的相关参数赋值,如id
          private int id;

     private EmployeeService employeeService;

     public void setEmployeeService(EmployeeService employeeService) {
           this.employeeService = employeeService;
     }
      //所以当触发save action方法时会在Preparable的拦截器中自动查找action中是否包含
                    prepareSave()或prepareDoSave() 方法如果包含这个方法就先执行prepareSave()
                                        prepareDoSave()
     public String save() {
          System. out.println(employee );
           employeeService.save(employee );
           return "save-success" ;
     }
     //通过观察源码执行过程得知 
    // PrepareInterceptor 拦截器将调用 prepare() 方法,prepareActionMethodName()方法 或 prepareDoActionMethodName ()方法 
     private Employee employee;
     public void prepareSave(){
          //所以在这里进行初始化Model的类
           employee= new Employee();
     } 
      
     
     @Override
     public Employee getModel() {
           return employee ;
     }
    //  若 Action 实现 Preparable 接口,则 Action 方法需实现 prepare() 方法
//2. prepare拦截器执行prepare方法,prepare方法中会根据参数,如id,去调用业务逻辑,设置model对象 但是一般这个方法都不进行执行具体的方法内容
//PrepareInterceptor 拦截器会根据 alwaysInvokePrepare 属性决定是否执行prepare()方法
     @Override
     public void prepare() throws Exception {}
}

源码实现:
public class PrepareInterceptor extends MethodFilterInterceptor {
 
PrepareInterceptor 拦截器根据 firstCallPrepareDo  属性决定获取 prepareActionMethodName 

、 prepareDoActionMethodName的顺序。默认情况下先获取 prepareActionMethodName (),

 如果没有该方法,就寻找prepareDoActionMethodName()。
 如果找到对应的方法就调用该方法

    private final static String PREPARE_PREFIX = "prepare";
    private final static String ALT_PREPARE_PREFIX = "prepareDo";
     // 拦截器会根据 alwaysInvokePrepare 属性决定是否执行prepare()方法
    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 {
        Object action = invocation.getAction();

        if (action instanceof Preparable) {
            try {
                String[] prefixes;
               // firstCallPrepareDo = false;
                if (firstCallPrepareDo ) {
                    prefixes = new String[] {ALT_PREPARE_PREFIX , PREPARE_PREFIX};
                } else {
                    prefixes = new String[] {PREPARE_PREFIX, ALT_PREPARE_PREFIX };
                }
               //会先执行prefixes 数组在PrefixMethodInvocationUtil的invokePrefixMethod
                    方法中
                PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes);
            }
            catch (InvocationTargetException e) {
                 throw e;
            }
          //拦截器会根据 alwaysInvokePrepare 属性决定是否执行prepare()方法
            if (alwaysInvokePrepare ) {
                ((Preparable) action).prepare();
            }
        }

        return invocation.invoke();
    }
//执行prefixes 数组在PrefixMethodInvocationUtil类中
默认执行方法
private static final String DEFAULT_INVOCATION_METHODNAME = "execute";

public static void invokePrefixMethod(ActionInvocation actionInvocation, String[] prefixes) {
          //得到请求的action类对象
          Object action = actionInvocation. getAction();
          //得到请求的action类对象请求的方法名
          String methodName = actionInvocation.getProxy().getMethod(); 
          //如果方法名称为NUll就赋值默认的execute方法
           if (methodName == null) {
               
             methodName = DEFAULT_INVOCATION_METHODNAME;
          }
          //得到前缀方法 也就是prepareSave()或prepareDoSave() 方法如果包含这个方法就先执 行prepareSave()prepareDoSave()
                      
          也就是在请求方action的方法加上prepareActionMethodName()方法 或 prepareDoActionMethodName ()方法  通过getPrefixedMethod 这个方法进行的装配
          Method method = getPrefixedMethod (prefixes, methodName, action);
          //如果包含prepareSave()或prepareDoSave() 方法如果包含这个方法就先执 行prepareSave()prepareDoSave()就进行调用这个对应的方法
               
           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) {
          //在这里进行配置成 prepareActionMethodName()方法 或 prepareDoActionMethodName 
            String prefixedMethodName = prefixe + capitalizedMethodName;
            try {
                return action.getClass().getMethod(prefixedMethodName, EMPTY_CLASS_ARRAY);
            }
            catch (NoSuchMethodException e) {
                 
                }
            }
        }
           return null ;
     }
 
以上就是ModelDriven 和 Preparable 拦截器的使用和源码的原理 个人感觉写的比较繁琐了

 

ModelDriven 和 Preparable 拦截器

标签:

原文地址:http://www.cnblogs.com/lflx/p/4398195.html

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