Struts2 运行流程图-1

ActionProxy 是 Action 的一个代理类,也就是说Action的调用是通过 ActionProxy 实现的,其实就是调用了ActionProxy.execute()方法,而该方法又调用了ActionInvocation.invoke()方法.

ActionInvocation就是Action的调用者。ActionInvocation在Action的执行过程中,负责Interceptor、Action和Result等一系列元素的调度。

默认的拦截器栈

Params 拦截器

  Parameters 拦截器将把表单字段映射到 ValueStack 栈的栈顶对象的各个属性中. 如果某个字段在模型里没有匹配的属性, Param 拦截器将尝试 ValueStack 栈中的下一个对象

把 Action 和 Model 隔开

  在使用 Struts 作为前端的企业级应用程序时把 Action 和 Model 清晰地隔离开是有必要的: 有些 Action 类不代表任何Model 对象, 它们的功能仅限于提供显示服务

  如果 Action 类实现了 ModelDriven 接口,该拦截器将把 ModelDriven 接口的 getModel() 方法返回的对象置于栈顶

ModelDriven 拦截器

Action 实现 ModelDriven 接口后的运行流程

  1). 先会执行 ModelDrivenInterceptor 的 intercept 方法.

 1     public String intercept(ActionInvocation invocation) throws Exception {
 2         //获取 Action 对象: EmployeeAction 对象, 此时该 Action 已经实现了 ModelDriven 接口
 3         //public class EmployeeAction implements RequestAware, ModelDriven<Employee>
 4         Object action = invocation.getAction();
 5
 6         //判断 action 是否是 ModelDriven 的实例
 7         if (action instanceof ModelDriven) {
 8             //强制转换为 ModelDriven 类型
 9             ModelDriven modelDriven = (ModelDriven) action;
10             //获取值栈
11             ValueStack stack = invocation.getStack();
12             //调用 ModelDriven 接口的 getModel() 方法
13             //即调用 EmployeeAction 的 getModel() 方法
14             /*
15             public Employee getModel() {
16                 employee = new Employee();
17                 return employee;
18             }
19             */
20             Object model = modelDriven.getModel();
21             if (model !=  null) {
22                 //把 getModel() 方法的返回值压入到值栈的栈顶. 实际压入的是 EmployeeAction 的 employee 成员变量
23                 stack.push(model);
24             }
25             if (refreshModelBeforeResult) {
26                 invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model));
27             }
28         }
29         return invocation.invoke();
30     }

View Code

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

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

public Employee getModel() {return new Employee();
}

Preparable 拦截器

  Struts 2.0 中的 modelDriven 拦截器负责把 Action 类以外的一个对象压入到值栈栈顶, 而 prepare 拦截器负责准备为 getModel() 方法准备 model

PrepareInterceptor拦截器用方法

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

  PrepareInterceptor 拦截器将调用 prepare() 方法,prepareActionMethodName()方法 或 prepareDoActionMethodName ()方法

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

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

使用 paramsPrepareParamsStack 拦截器栈

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

使用 paramsPrepareParamsStack 拦截器栈后的运行流程

  1). paramsPrepareParamsStack 和 defaultStack 一样都是拦截器栈. 而 struts-default 包默认使用的是 defaultStack

  2). 可以在 Struts 配置文件中通过以下方式修改使用的默认的拦截器栈

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

  3). paramsPrepareParamsStack 拦截器在于  params -> modelDriven -> params

    所以可以先把请求参数赋给 Action 对应的属性, 再根据赋给 Action 的那个属性值决定压到值栈栈顶的对象, 最后再为栈顶对象的属性赋值.

对于 edit 操作而言:

  I.   先为 EmployeeAction 的 employeeId 赋值
  II.  根据 employeeId 从数据库中加载对应的对象, 并放入到值栈的栈顶
  III. 再为栈顶对象的 employeeId 赋值(实际上此时 employeeId 属性值已经存在)
  IV.  把栈顶对象的属性回显在表单中.

流程如下:

–1. params拦截器首先给action中的相关参数赋值,如id
–2. prepare拦截器执行prepare方法,prepare方法中会根据参数,如id,去调用业务逻辑,设置model对象
–3. modelDriven拦截器将model对象压入value stack,这里的model对象就是在prepare中创建的
–4. params拦截器再将参数赋值给model对象
–5. action的业务逻辑执行

  4). 关于回显: Struts2 表单标签会从值栈中获取对应的属性值进行回显.

使用 PrepareInterceptor 和 Preparable 接口.

关于 PrepareInterceptor     [分析后得到的结论]

  若 Action 实现了 Preparable 接口, 则 Struts 将尝试执行 prepare[ActionMethodName] 方法,

  若 prepare[ActionMethodName] 不存在, 则将尝试执行 prepareDo[ActionMethodName] 方法.   若都不存在, 就都不执行.

  若 PrepareInterceptor  的 alwaysInvokePrepare 属性为 false, 则 Struts2 将不会调用实现了 Preparable 接口的  Action 的 prepare() 方法

可以为每一个 ActionMethod 准备 prepare[ActionMethdName] 方法, 而抛弃掉原来的 prepare() 方法, 将 PrepareInterceptor  的 alwaysInvokePrepare 属性置为 false, 以避免 Struts2 框架再调用 prepare() 方法.

如何在配置文件中为拦截器栈的属性赋值: 参看 /struts-2.3.15.3/docs/WW/docs/interceptors.html.

例子:

        <!-- 配置使用 paramsPrepareParamsStack 作为默认的拦截器栈 --><!-- 修改 PrepareInterceptor 拦截器的 alwaysInvokePrepare 属性值为 false --><interceptors><interceptor-stack name="atguigustack"><interceptor-ref name="paramsPrepareParamsStack"><param name="prepare.alwaysInvokePrepare">false</param></interceptor-ref></interceptor-stack></interceptors><default-interceptor-ref name="atguigustack"/>

源代码解析

 1 public String doIntercept(ActionInvocation invocation) throws Exception {
 2     //获取 Action 实例
 3     Object action = invocation.getAction();
 4
 5     //判断 Action 是否实现了 Preparable 接口
 6     if (action instanceof Preparable) {
 7         try {
 8             String[] prefixes;
 9             //根据当前拦截器的 firstCallPrepareDo(默认为 false) 属性确定 prefixes
10             if (firstCallPrepareDo) {
11                 prefixes = new String[] {ALT_PREPARE_PREFIX, PREPARE_PREFIX};
12             } else {
13                 prefixes = new String[] {PREPARE_PREFIX, ALT_PREPARE_PREFIX};
14             }
15             //若为 false, 则 prefixes: prepare, prepareDo
16             //调用前缀方法.
17             PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes);
18         }
19         catch (InvocationTargetException e) {
20
21             Throwable cause = e.getCause();
22             if (cause instanceof Exception) {
23                 throw (Exception) cause;
24             } else if(cause instanceof Error) {
25                 throw (Error) cause;
26             } else {
27                 throw e;
28             }
29         }
30
31         //根据当前拦截器的 alwaysInvokePrepare(默认是 true) 决定是否调用 Action 的 prepare 方法
32         if (alwaysInvokePrepare) {
33             ((Preparable) action).prepare();
34         }
35     }
36
37     return invocation.invoke();
38 }

View Code

PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes) 方法:

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 executemethodName = DEFAULT_INVOCATION_METHODNAME;}//获取前缀方法Method method = getPrefixedMethod(prefixes, methodName, action);//若方法不为 null, 则通过反射调用前缀方法if (method != null) {method.invoke(action, new Object[0]);}
}

View Code

PrefixMethodInvocationUtil.getPrefixedMethod 方法:

public static Method getPrefixedMethod(String[] prefixes, String methodName, Object action) {assert(prefixes != null);//把方法的首字母变为大写String capitalizedMethodName = capitalizeMethodName(methodName);//遍历前缀数组for (String prefixe : prefixes) {//通过拼接的方式, 得到前缀方法名: 第一次 prepareUpdate, 第二次 prepareDoUpdateString prefixedMethodName = prefixe + capitalizedMethodName;try {//利用反射获从 action 中获取对应的方法, 若有直接返回. 并结束循环.return action.getClass().getMethod(prefixedMethodName, EMPTY_CLASS_ARRAY);}catch (NoSuchMethodException e) {// hmm -- OK, try next prefixif (LOG.isDebugEnabled()) {LOG.debug("cannot find method [#0] in action [#1]", prefixedMethodName, action.toString());}}}return null;
}

View Code

转载于:https://www.cnblogs.com/linyueshan/p/5686623.html

struts2 ModelDriven 和 Preparable 拦截器相关推荐

  1. 使用struts2中默认的拦截器以及自定义拦截器

    转自:http://blog.sina.com.cn/s/blog_82f01d350101echs.html 如何使用struts2拦截器,或者自定义拦截器.特别注意,在使用拦截器的时候,在Acti ...

  2. 【struts2】预定义拦截器

    1)预定义拦截器 Struts2有默认的拦截器配置,也就是说,虽然我们没有主动去配置任何关于拦截器的东西,但是Struts2会使用默认引用的拦截器.由于Struts2的默认拦截器声明和引用都在这个St ...

  3. struts2模型驱动和令牌拦截器

    模型驱动: *要从页面中获取表单元素的值,需要在动作类中声明与页面元素同名的属性.导致动作类中既有javabean又有业务方法. *将javabean和业务方法进行分离: *将重新创建一个javabe ...

  4. Struts2——(7)拦截器组件

    AOP:面向切面编程(通过配置文件来指定作用到目标对象) OOP:面向对象编程 AOP具有很好的可插拔特性,很灵活. 可用于封装共通的业务处理,之后可以通过配置作用到Action组件上. 共通的业务处 ...

  5. struts2(三) 输入校验和拦截器

    前面知道了struts2的架构图和struts2的自动封装表单参数和数据类型自动转换,今天来学struts2的第三第四个东西,输入校验和拦截器, --WH 一.输入校验 在以前我们写一个登录页面时,并 ...

  6. Struts2 源码分析——拦截器的机制

    本章简言 上一章讲到关于action代理类的工作.即是如何去找对应的action配置信息,并执行action类的实例.而这一章笔者将讲到在执行action需要用到的拦截器.为什么要讲拦截器呢?可以这样 ...

  7. Struts2入门(二)——配置拦截器

    一.前言 之前便了解过,Struts 2的核心控制器是一个Filter过滤器,负责拦截所有的用户请求,当用户请求发送过来时,会去检测struts.xml是否存在这个action,如果存在,服务器便会自 ...

  8. 在struts2中配置自定义拦截器放行多个方法

    源码: 自定义的拦截器类: //自定义拦截器类:LoginInterceptor ; package com.java.action.interceptor; import javax.servlet ...

  9. struts2开发4--自定义拦截器把不文明用语改变成***

    struts2拦截器是在访问某个Action或者Action的某个方法.字段之前或者之后实施拦截,并且struts2拦截器是可插拔的,拦截器是AOP的一种实现.这里重点介绍下自定义文字过滤拦截器,把我 ...

最新文章

  1. 洛谷 P1208混合牛奶【贪心】
  2. tomcat 外网访问不了_免费云服务器/jdk环境配置/Tomcat简单配置
  3. Swift - 14 - 字符串的基础操作
  4. 选择开发语言和学习的路径(这个标题可能有点不准确)
  5. 使用 Eclipse 建立包的时候,“name”下可选项“Create package-info.java”的作用是什么?
  6. web.config中namespace的配置(针对页面中引用)
  7. mysql 取字符串首字母_MySQL通过函数获取字符串汉字拼音首字母大写字符串
  8. FlexSIM.ED v4.0 1CD(生产排程,虚拟管理,能力平衡软件)
  9. Windows 相关镜像及补丁下载地址
  10. Tomcat发布项目时,浏览器地址栏图标的问题
  11. Arcgis学习视频
  12. Android studio进行文件,代码对比
  13. CUGBACM130715 组队赛 BNU Curvy Little Bottles - from lanshui_Yang
  14. Android之Dex动态加载机制解析
  15. CSS峰会圆桌论道丨共享产业数字化升级中的安全探索
  16. P4086 [USACO17DEC]My Cow Ate My Homework S(静态数据预处理:后缀和、后缀最小值)
  17. 微信小程序:音乐播放器带进度条
  18. 便携式计算机的基本知识,使用便携式计算机,错误的做法是()A、非涉密便携机不得存储或处理涉密信息B、涉密便携机需经过保 - 普法考试题库问答...
  19. 公租房租赁合同怎么填
  20. (三)Horizon 队列管理工具

热门文章

  1. jQuery Post
  2. Autolayout代码实现举例-01使用线性公式
  3. STL(1)——查找函数find的使用
  4. 创建你的第一个游戏Pong——让我们编写Pong
  5. 玩转jquery插件之flexigrid 【转】
  6. AJAX(异步的 JavaScript 和 XML)
  7. JavaScript学习(三十八)—面向过程与面向对象
  8. python3读取本地_Python3 获取本机 IP
  9. csh shell_Shell基础知识
  10. 手机的余存电量还有多少的时候适合充电?