ModelDriven和Preparable拦截器

  • 原理剖析
    • Struts2运行机制

[该图片请放大查看]

  • 拦截器和拦截器栈

①打个比方:我们去医院体检,需要检查很多个项目,比如:测血压、胸透、做心电图、测身高、测体重、测肺活量等等,经历这一系列的检查后,最终才能拿到体检报告。

这里体检的项目就相当于我们Struts2里面的拦截器,若干个拦截器组织在一起就是一个拦截器栈,最终的体检报告相当于目标Action。

②在Struts2中,默认的拦截器栈是:struts-default.xml中定义的defaultStack,这里只列举了其中一部分

<interceptor-stack name="defaultStack">

<interceptor-ref name="exception"/>

<interceptor-ref name="alias"/>

<interceptor-ref name="servletConfig"/>

<interceptor-ref name="i18n"/>

<interceptor-ref name="prepare"/>

<interceptor-ref name="chain"/>

<interceptor-ref name="scopedModelDriven"/>

<interceptor-ref name="modelDriven"/>

……

<interceptor-ref name="params">

……

</interceptor-stack>

  • params拦截器

params拦截器有两项功能

①提交表单时将表单域中的值注入到栈顶对象对应的属性中

②目标页面为表单时,将栈顶对象的属性回显到对应的表单域中

  • ModelDriven拦截器

getModel()方法是由谁来调用的呢?通过在getModel()方法内设置断点,以Debug模式运行项目发现:

 找到ModelDrivenInterceptor.intercept()方法:

总体流程:

  • 改进edit()方法
    • 问题分析

问题产生的原因是:在getModel()方法中将一个手动创建的空对象提供给ModelDriven拦截器,单调死板。

  • 需求:当目标action方法是edit()时,将从数据库中取出的JavaBean压入栈顶,当目标action方法是add()、update()时将一个空的JavaBean压入栈顶
  • 解决问题

借助Preparable拦截器,为ModelDriven拦截器“准备”不同action方法所需要的不同JavaBean

  • Preparable拦截器使用方法:

①Action类实现com.opensymphony.xwork2.Preparable接口

②为不同的action方法声明专门的prepare方法

③修改StudentAction类代码

  [1]prepare方法

 [2]getModel()方法

 [3]action()方法

  • Preparable拦截器工作流程

  • 自定义prepare方法的命名格式有什么玄机?

  • 深情的呼唤:急需在prepare拦截器之前有一个params拦截器

  • paramsPrepareParamsStack 拦截器栈

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

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

配置方法

在struts.xml文件中,package标签下添加

<default-interceptor-ref name="paramsPrepareParamsStack">

</default-interceptor-ref>

  • paramsPrepareParamsStack 拦截器栈工作流程

  • 最终的工作流程

  • 补充:如何取消prepare()方法的执行

在struts.xml文件中action标签内配置:

<!-- 覆盖prepare 拦截器的alwaysInvokePrepare 参数值为 false -->

<interceptor-ref name="paramsPrepareParamsStack">

<param name="prepare.alwaysInvokePrepare">false</param>

</interceptor-ref>