Struts2拦截器之ModelDrivenInterceptor
叙述套路:
1.这是个啥东西,它是干嘛用的?
2.我知道它能干啥了,那它咋个用呢?
3.它能跑起来了,但是它是咋跑起来的是啥原理呢?
一、ModelDriven是个啥?他能做什么?
从前端页面到后端的参数传递可以分为属性驱动和对象驱动(我瞎编的词(*^__^*) ),属性驱动的意思就是参数从前端传递到后台之后仍然还是参数,需要我们自己按需组装成对象,对象驱动的意思就是参数从前端传递后台之后就自动被组装为了一个对象,ModelDriven实现的就是对象驱动(专业的说法叫模型驱动,但是这个词一看就很不好懂的样子),它做的事情很简单就是在参数拦截器设置参数之前将一个特定的对象放到值栈的栈顶。因为值栈元素有暴露栈中对象属性的特性,所以我们在前端页面中就可以直接访问这个对象的属性而不是还要用对象名导航了,比如user.name可以直接使用name访问。
二、ModelDriven的使用
使用ModelDriven很简单,只要action实现了一个ModelDriven接口即可,这个接口的代码如下:
package com.opensymphony.xwork2; /**
* ModelDriven Actions provide a model object to be pushed onto the ValueStack
* in addition to the Action itself, allowing a FormBean type approach like Struts.
*
* @author Jason Carreira
*/
public interface ModelDriven<T> { /**
* Gets the model to be pushed onto the ValueStack instead of the Action itself.
*
* @return the model
*/
T getModel(); }
这种类似的接口由很多,它们的作用大致如下:
1.类型标识,在别的地方可以使用instanceof来判断一个实例是否实现了某一个接口,这个时候接口起到类型标识的作用。
2.统一动作,使得某一类action可以有统一的动作,比如ModelDriven都可以返回一个对象,Validateable都可以验证数据等等。
需要注意的是ModelDriven是一个泛型接口,所以我们还是尽量在实现的时候就指定类型比较好。
回到正题,来看一个实现了ModelDriven的action:
public class LoginAction extends ActionSupport implements ModelDriven<User> { private User user; public String login() { return SUCCESS;
} @Override
public User getModel() {
if(user==null) user=new User();
return user;
}
public User getUser() {
return user;
} public void setUser(User user) {
this.user = user;
} }
这个时候在前端页面上就可以直接访问user的username,passwd等等,比如:
<form action="loginAction" method="post">
用户名:<input type="text" name="username" /><br/>
密 码:<input type="password" name="passwd" /><br/>
<input type="submit" value="登录" />
</form>
后台接收到的就是完整的User对象,这只是参数接收的一种方式。
ModelDriven注意事项:执行结果前刷新
如果在execute()等映射到的方法中改变了成员属性user的引用的话,就需要在执行结果前刷新,比如:
public String login() { user=userService.login(user); return SUCCESS;
}
登录的功能是根据username和passwd查询一个新的对象,如果为null说明登录失败,否则返回一个user对象里面装满了此用户的各种信息,但是不论如何,成员属性user的引用已经被修改,想一下当这个方法执行完的时候Action中的成员属性user指向的是一个引用,而ValueStack中的对象user指向的是另一个引用,而我们想要的是等下访问栈顶元素就可以访问到刚刚在login()中查出的那个对象呢,很明显,这个结果是做不到这一点的,那么该怎么办呢?就是在执行Result之前将我们的成员变量user的引用(可能已经被修改了)重新放入到栈顶:
需要修改配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="default" namespace="/" extends="struts-default" abstract="false"> <interceptors>
<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="debugging"/>
<interceptor-ref name="scopedModelDriven"/>
<!-- 需要传入refreshModelBeforeResult为true,默认为false -->
<interceptor-ref name="modelDriven">
<param name="refreshModelBeforeResult">true</param>
</interceptor-ref>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*,^struts\..*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
</interceptor-stack>
</interceptors> <action name="loginAction" class="struts_practice_001.LoginAction" method="login">
<result>/success.jsp</result>
<result name="input">/login.jsp</result>
<result name="error">/error.jsp</result>
</action>
</package> </struts>
可以使用<s:debug />标签在结果jsp页面查看值栈中的数据已经被刷新为新查询出来的user对象。
总结一下:
1.ModelDriven的实现需要实现一个叫做ModelDriven的接口,然后在getModel()返回一个压入栈顶的对象。
2.如果中间修改了这个Model变量的引用,就需要在执行Result前刷新,所谓刷新就是将最新的Model对象压入栈顶。(需要注意的是如果不是修改的引用的话根本没必要进行刷新,因为只要引用相同的话就可以顺着这个引用找过来,找到的属性还是最新的属性,因为它们只是多个指针指向的堆中的同一块对象内存)
三、ModelDriven的原理
ModelDriven的实现依靠ModelDrivenInterceptor,ModelDrivenInterceptor代码剖析:
/**
* ModelDrivenInterceptor:
* 这个拦截器的作用是将一个对象放到ValueStack的栈顶暴露其属性以便直接访问
*/
public class ModelDrivenInterceptor extends AbstractInterceptor { //默认是不刷新的
protected boolean refreshModelBeforeResult = false; //配置的时候可以传入refreshModelBeforeResult参数设置是否需要在执行Result前刷新
public void setRefreshModelBeforeResult(boolean val) {
this.refreshModelBeforeResult = val;
} @Override
public String intercept(ActionInvocation invocation) throws Exception {
//取得当前的action实例
Object action = invocation.getAction(); //判断action是否实现了ModelDriven接口
if (action instanceof ModelDriven) {
//将action实例转为ModelDriven实例
ModelDriven modelDriven = (ModelDriven) action;
//获取值栈
ValueStack stack = invocation.getStack();
//获取Model
Object model = modelDriven.getModel();
//可能是为了避免空指针之类的吧,所以喽,ModelDriven拦截器不会自动创建对象(需注意与action成员变量有区别)
if (model != null) {
//将Model放到值栈的栈顶
stack.push(model);
}
//判断是否需要在执行结果之前之前刷新
if (refreshModelBeforeResult) {
//如果需要刷新的,添加一个在执行Result之前要执行的回调对象(对象中有一个回调函数)
//我们只需要知道传入的这个对象的beforeResult():void方法要在执行Result之前被调用
invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model));
}
}
return invocation.invoke();
} /**
* Refreshes the model instance on the value stack, if it has changed
* 如果在
*/
protected static class RefreshModelBeforeResult implements PreResultListener {
//原始Model对象
private Object originalModel = null;
//当前的Action实例
protected ModelDriven action; public RefreshModelBeforeResult(ModelDriven action, Object model) {
this.originalModel = model;
this.action = action;
} //在执行结果之前调用的回调方法
public void beforeResult(ActionInvocation invocation, String resultCode) {
//获得值栈
ValueStack stack = invocation.getStack();
CompoundRoot root = stack.getRoot(); //假设需要刷新
boolean needsRefresh = true;
//拿到最新的Model对象
Object newModel = action.getModel(); // Check to see if the new model instance is already on the stack
//检查如果这个最新的Model可以在栈中找得到,说明根本就没变,所以就不用刷新了
for (Object item : root) {
if (item.equals(newModel)) {
needsRefresh = false;
}
} // Add the new model on the stack
//如果需要将最新的Model对象压入栈顶的话
if (needsRefresh) { // Clear off the old model instance
//首先移除原来的Model对象
if (originalModel != null) {
root.remove(originalModel);
}
//然后将最新的Model对象压入栈顶
if (newModel != null) {
stack.push(newModel);
}
}
}
}
}
Struts2拦截器之ModelDrivenInterceptor的更多相关文章
- Struts2拦截器之FileUploadInterceptor
一.它能做什么? 借助于这个拦截器我们可以实现文件的上传和下载功能. 理论部分: struts2的文件上传下载功能也要依赖于Apache commons-fileupload和Apache commo ...
- Struts2拦截器之DefaultWorkflowInterceptor
一.DefaultWorkflowInterceptor是什么 首先说这东西是干嘛来的,在action中可以对传进来的数据进行验证,方法是实现Validateable接口的validate():voi ...
- Struts2拦截器之ExceptionMappingInterceptor(异常映射拦截器)
一.异常拦截器是什么? 异常拦截器的作用是提供一个机会,可以设置在action执行过程中发生异常的时候映射到一个结果字符串而不是直接中断. 将异常整合到业务逻辑中,比如在分层系统的调用中可以从底层抛出 ...
- Struts2拦截器的执行过程浅析
在学习Struts2的过程中对拦截器和动作类的执行过程一度陷入误区,特别读了一下Struts2的源码,将自己的收获分享给正在困惑的童鞋... 开始先上图: 从Struts2的图可以看出当浏览器发出请求 ...
- Struts2拦截器总结<转>
由于项目中在登录跳转到其他应用程序模块的时候有用到拦截器,因此查看了一下相关资料. 原文地址:http://blog.csdn.net/sendfeng/article/details/4248120 ...
- Struts2 拦截器具体配置过程
拦截器差点儿遍布每个程序中,所以贴出拦截器配置的具体过程,希望可以帮到大家. Struts2 拦截器具体配置过程 <interceptors> <!-- 先定义拦截器 --> ...
- Struts2拦截器详解
一.Struts2拦截器原理: Struts2拦截器的实现原理相对简单,当请求struts2的action时,Struts 2会查找配置文件,并根据其配置实例化相对的 拦截器对象,然后串成一个列 ...
- 转载 - Struts2 拦截器详细配置过程
出处:http://www.blogjava.net/zzzlyr/archive/2009/10/12/297998.html Struts2 拦截器详细配置过程 1:所有拦截器的超级接口Inter ...
- Struts2 拦截器配置以及实现
@(Java ThirdParty)[Struts|Interceptor] Struts2 拦截器配置以及实现 Struts2的拦截器应用于Action,可以在执行Action的方法之前,之后或者两 ...
随机推荐
- 表单select相关
selectedIndex 属性可设置或返回下拉列表中被选选项的索引号. options[] 返回包含下拉列表中的所有选项的一个数组. add()向下拉列表添加一个选项. blur()从下拉列表移开焦 ...
- net-snmp配置:snmp v3的安全配置
net-snmp配置:snmp v3的安全配置 net-snmp配置:snmp v3的安全配置 增加snmp v3用户 增加 认证且加密只读账号(authPriv) 增加 认证且加密的读写账户 增加 ...
- win7 ubuntu 14.04双系统安装
安装win7和linux双系统,一般先安装win7,后安装linux,本片就是指在安装好win7的情况下,安装ubuntu. 准备材料: EasyBCD软件 ubuntu14.04iso镜像文件,64 ...
- json_decode()和json_encode()的使用方法
json_decode对JSON格式的字符串进行编码 json_encode对变量进行 JSON 编码 JS中对JSON的解析 一.JSON字符串转换为JSON对象 要运用上面的str1,必须 ...
- php多线程抓取信息测试例子
php多线程抓取信息测试例子 PHP 5.3 以上版本,使用pthreads PHP扩展,可以使PHP真正地支持多线程.多线程在处理重复性的循环任务,能够大大缩短程序执行时间. PHP扩展下载:htt ...
- 保存字符串到手机SDcard为txt文件
try { if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { File sdCardDir ...
- OS10.11系统下 安装cocoapods 以及 安装cocoapods-xcode-plugin-master插件来加载三方框架
http://www.cnblogs.com/cheng923181/p/4883476.html OS10.11系统下 安装cocoapods 以及 安装cocoapods-xcode-plugin ...
- 怎么安装MYSQL5.0的JDBC驱动
1.下载mysql for jdbc driver. http://dev.mysql.com/downloads/connector/j/3.1.htmlMySQL Connector/J is ...
- oracle异常记录
2015年9月14 1.在csdn论坛中看帖子,遇到的一个问题:ORA-01422 实际返回的行数超出请求的行数.
- 【Unity3D】计算二维向量夹角(-180到180)
在Unity3D中,有时候我们需要计算二维向量的夹角.二维向量夹角一般在0~180度之前,可以直接调用Vector2.Angle(Vector2 from, Vector2 to)来计算. 但是在有些 ...