从源代码分析modelDriven拦截器和params拦截器和拦截器prepare 和paramsPrepareParamsStack拦截器栈(让你的Struts2代码更简洁——如何培养框架设计能力
源代码文件:Web App Libraries/struts2-core-2.3.15.3.jar/struts-default.xml
|
拦截器modelDriven: <interceptor |
|
拦截器params: <interceptor |
|
拦截器prepare: <interceptor |
|
拦截器栈: <interceptor-stack <interceptor-ref <interceptor-ref <interceptor-ref <interceptor-ref <interceptor-ref <interceptor-ref <param </interceptor-ref> <interceptor-ref <interceptor-ref <interceptor-ref <interceptor-ref <interceptor-ref <interceptor-ref <interceptor-ref <interceptor-ref <param </interceptor-ref> <interceptor-ref <interceptor-ref <param </interceptor-ref> <interceptor-ref <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 private private private private private public this.alwaysInvokePrepare = Boolean.parseBoolean(alwaysInvokePrepare); } public this.firstCallPrepareDo = Boolean.parseBoolean(firstCallPrepareDo); } @Override public String doIntercept(ActionInvocation invocation) //获取Action对象 Object action = invocation.getAction(); //推断Action是否实现了preparable接口 if (action try { String[] prefixes; //依据当前拦截器的 firstCallPrepareDo(默觉得 false) if (firstCallPrepareDo) { prefixes = new String[] {ALT_PREPARE_PREFIX, } else { prefixes = new String[] {PREPARE_PREFIX, } //若为 false, //调用前缀方法. PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause throw (Exception) cause; } else throw (Error) cause; } else { throw e; } } //依据当前拦截器的 alwaysInvokePrepare(默认是 true) 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 private private private public //获取 Action Object action = actionInvocation.getAction(); //获取要调用的 Action String methodName = actionInvocation.getProxy().getMethod(); if (methodName == // 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 != method.invoke(action, new Object[0]); } } public assert(prefixes != //把方法的首字母变为大写 String capitalizedMethodName =capitalizeMethodName(methodName); //遍历前缀数组 for (String prefixe : prefixes) { String prefixedMethodName = prefixe + capitalizedMethodName; //通过拼接的方式, try { //利用反射获从 action return action.getClass().getMethod(prefixedMethodName, } catch (NoSuchMethodException e) { // hmm -- OK, try next prefix if (LOG.isDebugEnabled()) { LOG.debug("cannot find method [#0] in action [#1]", prefixedMethodName, action.toString()); } } } return } public assert(methodName != 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 protected public this.refreshModelBeforeResult = val; } @Override public String intercept(ActionInvocation invocation) //获取 Action //public class EmployeeAction implements RequestAware, ModelDriven<Employee> Object action = invocation.getAction(); //推断 action if (action //强制转换为 ModelDriven ModelDriven modelDriven = (ModelDriven) action; //获取值栈 ValueStack stack = invocation.getStack(); //调用 ModelDriven //即调用 EmployeeAction /* public Employee getModel() { employee = new Employee(); return employee; } */ Object model = modelDriven.getModel(); if (model != //把 getModel() 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 private Object protected ModelDriven public RefreshModelBeforeResult(ModelDriven action, Object model) { this.originalModel = model; this.action = action; } public ValueStack stack = invocation.getStack(); CompoundRoot root = stack.getRoot(); boolean needsRefresh = 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 != root.remove(originalModel); } if (newModel != stack.push(newModel); } } } } } |
细节凝视:
细节一:运行 ParametersInterceptor 的 intercept 方法: 把请求參数的值赋给栈顶对象相应的属性. 若栈顶对象没有相应的属性, 则查询值栈中下一个对象相应的属性...
细节二:getModel 方法不能提供下面实现. 的确会返回一个 Employee 对象到值栈的栈顶. 但当前 Action 的 employee 成员变量却是 null.
|
@Override public return } |
版权声明:本文博客原创文章,博客,未经同意,不得转载。
从源代码分析modelDriven拦截器和params拦截器和拦截器prepare 和paramsPrepareParamsStack拦截器栈(让你的Struts2代码更简洁——如何培养框架设计能力的更多相关文章
- Android AsyncTask 源代码分析
AsyncTask源代码分析 public abstract class AsyncTask<Params, Progress, Result> { //日志TAG private sta ...
- struts2 paramsPrepareParamsStack拦截器简化代码(源码分析)
目录 一.在讲 paramsPrepareParamsStack 之前,先看一个增删改查的例子. 1. Dao.java准备数据和提供增删改查 2. Employee.java 为model 3. E ...
- SDL2源代码分析6:复制到渲染器(SDL_RenderCopy())
===================================================== SDL源代码分析系列文章列表: SDL2源代码分析1:初始化(SDL_Init()) SDL ...
- SDL2源代码分析3:渲染器(SDL_Renderer)
===================================================== SDL源代码分析系列文章列表: SDL2源代码分析1:初始化(SDL_Init()) SDL ...
- XBMC源代码分析 7:视频播放器(dvdplayer)-输入流(以libRTMP为例)
前文分析了XBMC的基本结构: XBMC源代码分析 1:整体结构以及编译方法 XBMC源代码分析 2:Addons(皮肤Skin) XBMC源代码分析 3:核心部分(core)-综述 XBMC源代码分 ...
- XBMC源代码分析 6:视频播放器(dvdplayer)-文件头(以ffmpeg为例)
XBMC分析系列文章: XBMC源代码分析 1:整体结构以及编译方法 XBMC源代码分析 2:Addons(皮肤Skin) XBMC源代码分析 3:核心部分(core)-综述 XBMC源代码分析 4: ...
- XBMC源代码分析 4:视频播放器(dvdplayer)-解码器(以ffmpeg为例)
XBMC分析系列文章: XBMC源代码分析 1:整体结构以及编译方法 XBMC源代码分析 2:Addons(皮肤Skin) XBMC源代码分析 3:核心部分(core)-综述 本文我们分析XBMC中视 ...
- 使用 paramsPrepareParamsStack 拦截器栈后的运行流程
2. 使用 paramsPrepareParamsStack 拦截器栈后的运行流程 1). paramsPrepareParamsStack 和 defaultStack 一样都是拦截器栈. 而 st ...
- 【Java EE 学习 69 上】【struts2】【paramsPrepareParamsStack拦截器栈解决model对象和属性赋值冲突问题】
昨天有同学问我问题,他告诉我他的Action中的一个属性明明提供了get/set方法,但是在方法中却获取不到表单中传递过来的值.代码如下(简化后的代码) public class UserAction ...
随机推荐
- [Oracle] 接线表
于OLTP制,嵌套连接占70%左右,哈希联接占20%,合并排序连接帐户10%. 嵌套连接 算法:嵌套连接从两个表分选出小表为驱动表,大表为被驱动表.先訪问驱动表(仅仅訪问1次).然后依据驱动表返回的行 ...
- live555 for Android
因为Live555 包中未提供Android 的config 所以编译器来比較麻烦,须要自己编写Android.mk ,下面是我通过 改动 现有的config文件,在cygwin实现 编译的过程,co ...
- HTML contact form with CAPTCHA
http://www.html-form-guide.com/contact-form/html-contact-form-captcha.html#codedownload
- Fitnesse用系列三
动态决策表 动态决策表是新出,版本号到今年年初还没有了.我看了看文档和演示文稿样本,其效果是作为一种辅助通用决策表.它不是easy匹配的名称和发射.但假设只有一个或两个参数.不管名字怎么都找不到,这并 ...
- Java实现字符全阵列阵列
import org.junit.Test; public class AllSort { public void permutation(char[] buf, int start, int end ...
- EXCEL随机密码生成函数
=CHAR(INT(RAND()*+))&INT(RAND()*+)&CHAR(INT(RAND()*+))&INT(RAND()*+)&CHAR(INT(RAND() ...
- 给EasyUI的DateBox控件添加清除button
EasyUI中间DateBox控制甚至没有被清除button.例如下面的附图: 真是不可思议,对于要求日期格式必须选择的情况下,不能清空日期,很不方便. 尽管能够通过手工改动EasyU ...
- Unity 捕获IronPython脚本错误
using System; using System.Collections.Generic; using System.IO; using System.Reflection; using Syst ...
- 基于Tkinter利用python实现颜色空间转换程序
主要基于colorsys实现,例子是从hls转换到rgb,假设要换颜色空间非常easy仅仅须要改动一个函数 用到了Scale和Canvas组件 代码例如以下: from Tkinter import ...
- [渣译文] SignalR 2.0 系列: SignalR简介
原文:[渣译文] SignalR 2.0 系列: SignalR简介 英文渣水平,大伙凑合着看吧,并不是逐字翻译的…… 这是微软官方SignalR 2.0教程Getting Started with ...