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的方法之前,之后或者两 ...
随机推荐
- java历史
1.产生: 1990年初sun公司James Gosling等员工开发java语言的雏形,最初被命名为Oak,定位于家用电器的控制和通讯,随后因为市场的需求,公司放弃计划,后面由于Internet的发 ...
- 剑指Offer 反转链表
题目描述 输入一个链表,反转链表后,输出链表的所有元素. 思路: 法1:用栈,压栈出栈 法2:头插法(有递归非递归2中) AC代码: /* struct ListNode { int va ...
- respberry pi3 上手随记
入门篇: raspberry 官网: https://www.raspberrypi.org/ 各个系统镜像下载地址: https://www.raspberrypi.org/downloads/ 树 ...
- Delphi结构体的扩展,可以自动初始化,反初始化,自定义拷贝函数.
转载:http://www.raysoftware.cn/?p=518&utm_source=tuicool 恭贺Delphi XE7诞生,Delphi XE7在编译器内部集成了我之前所实现的 ...
- bootstrap框架-----可见 隐藏
可见框架-像素选择 -block-inline :块内联元素 -inline-block将对象呈递为内联对象,但是对象的内容作为块对象呈递.旁边的内联对象会被呈递在同一行内,允许空格 可以设置宽度和 ...
- Asp.net MVC网站的基本结构
Asp.net MVC网站的基本结构:Controller->IBLL(定义业务接口)->BLL->DA(调用Utility)->数据库 ,其中A->B表示A调用B 1. ...
- 【Java】嵌套For循环性能优化案例
参考资料:http://cgs1999.iteye.com/blog/1596671 1 案例描述 某日,在JavaEye上看到一道面试题,题目是这样的:请对以下的代码进行优化 for (int i ...
- git日志输出格式及两个版本之间差异列表
查看commit id git log --pretty=format:"%h" git log --pretty=format:"%H" 获取两个版本间差异的 ...
- linux下用cronolog分割apache日志
linux下用cronolog分割apache日志,大神莫拍砖,菜鸟留一记录,小白请默默转载.连linux登陆和vi编辑都不会的,请默默关闭此页面.入正题 说明:淡绿色底的为linux命令,其他的为备 ...
- CEF3开发者系列之工程和代码结构
CEF支持一系列的编程语言和操作系统,并且能很容易地整合到新的或已有的工程中去.它的设计思想就是易用且兼顾性能. CEF3支持一系列的编程语言和操作系统,并且能很容易地整合到新的或已有的工程中去.它的 ...