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的方法之前,之后或者两 ... 
随机推荐
- leetcode 74. Search a 2D Matrix
			Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the follo ... 
- OpenCV摄像头人脸识别
			注: 从外设摄像装置中获取图像帧,把每帧的图片与人脸特征进行匹配,用方框框住识别出来的人脸 需要用到的函数: CvHaarClassifierCascade* cvLoadHaarClassifier ... 
- 极客DIY:制作一个可以面部、自主规划路径及语音识别的无人机
			引言 现在大部分无人机厂商都会为第三方开发者提供无人机API接口,让他们更容易地开发无人机飞行控制应用程序,让无人机想怎么玩就怎么玩.有的API接口可以帮助开发者开发基于Web版的APP.手机APP甚 ... 
- 【原创】ReFlux细说
			ReFlux细说 Flux作为一种应用架构(application architecture)或是设计模式(pattern),阐述的是单向数据流(a unidirectional data flow) ... 
- javascript最容易混淆的作用域、提升、闭包
			一.函数作用域 1.函数作用域 就是作用域在一个“Function”里,属于这个函数的全部变量都可以在整个函数的范围内使用及复用. function foo(a) { var b = 2; funct ... 
- centos 无线网卡安装,网卡rtl8188ee
			驱动: http://www.realtek.com.tw/downloads/downloadsView.aspx?Langid=1&PNid=48&PFid=48&Leve ... 
- mysql+mybatis+存储过程+事务 + 多并发流水号获取
			数据库存储过程 drop PROCEDURE generate_serial_number; CREATE PROCEDURE generate_serial_number( ), IN param_ ... 
- oracle 存储过程中调用存储过程
			create procedure sp_name() begin ……… end 比如: create procedure pro_showdbs() show datebase; end //用ex ... 
- [20160704]Addition program that use JOptionPane for input and output
			//Addition program that use JOptionPane for input and output. import javax.swing.JOptionPane; public ... 
- [k]css盒模型
			box-sizing : content-box || border-box || inherit 1.content-box:此值为其默认值.元素的宽度/高度(width/height)等于元素边 ... 
