从源代码分析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 ...
随机推荐
- ZOJ 3822 Domination(概率dp 牡丹江现场赛)
题目链接:problemId=5376">http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5376 Edward ...
- EXE文件结构和读取方法
一.EXE文件概念 EXE File英文全名executable file .译作可运行文件,可移植可运行 (PE) 文件格式的文件,它能够载入到内存中.并由操作系统载入程序运行,是可在操作系统存储空 ...
- Codeforces 432 D. Prefixes and Suffixes
用扩展KMP做简单省力..... D. Prefixes and Suffixes time limit per test 1 second memory limit per test 256 meg ...
- Springmvc +JNDI 在Tomcat下 配置数据源(转)
一. 简介 jndi(Java Naming and Directory Interface,Java命名和目录接口)是一组在Java应用中访问命名和目录服务的API.命名服务 ...
- NLB+Application Request Route 网路负载均衡
NLB网路负载均衡管理器详解 序言 在上一篇配置iis负载均衡中我们使用啦微软的ARR,我在那篇文章也中提到了网站的高可用性,但是ARR只能做请求入口的消息分发服务,这样如果我们的消息分发服务器给 ...
- AutoFac使用方法总结:Part III
生命周期 AutoFac中的生命周期概念非常重要,AutoFac也提供了强大的生命周期管理的能力. AutoFac定义了三种生命周期: Per Dependency Single Instance P ...
- cnBlog 的windows live writer 客户端配置
重装系统后总是忘,备个档 CNBLOG 博客名 cnblog 日志帐户 http://www.cnblogs.com/liulaocai2/ 用户:359444066 密码:同QQ密码,比QQ少一位 ...
- Linux下/proc目录简介(转)
1. /proc目录Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构.改变内核设置的机制.proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间.它以文 ...
- node.js基础:HTTP服务器
一个HTTP服务器响应 var http = require('http'); http.createServer(function(request,response){ response.end(' ...
- ASP.NET自定义控件组件开发 第三章 为控件添加事件 后篇
原文:ASP.NET自定义控件组件开发 第三章 为控件添加事件 后篇 第三章 为控件添加事件 后篇 前一篇文章只是简单的说了下事件,但是大家应该方法,在ASP.NET自定义控件中只是简单那么定义事件是 ...