继上篇博客介绍了值传递的几种形式,这篇博客,来说说struts2是如何实现的?
有这么两个概念和modelDriven实现有关:ValueStack,ModelDrivenInterceptor。
首先先介绍以下ModelDrivenInterceptor,该拦截器处于defaultStack第九的位置。ModelDrivenInterceptor拦截器主要做的事就是调用Action的getModel()方法然后把返回的model压入值栈。
下面是该拦截器intercept方法源码:
public class ModelDrivenInterceptor extends AbstractInterceptor {
protected boolean refreshModelBeforeResult = false;
public void setRefreshModelBeforeResult(boolean val) {
this.refreshModelBeforeResult = val;
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
Object action = invocation.getAction();
if (action instanceof ModelDriven) {
ModelDriven modelDriven = (ModelDriven) action;
ValueStack stack = invocation.getStack();
Object model = modelDriven.getModel();
if (model != null) {
stack.push(model);
}
if (refreshModelBeforeResult) {
invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model));
}
}
return invocation.invoke();
}
此方法就是就是把getModel方法返回的结果压入值栈而已,我们一般实现这个接口是利用压入值栈的model对象接收从页面提交过来的数据,因为Action也是在值栈中,而struts2在赋值参数的时候是在值栈从栈顶往栈底寻找有相应setter方法的对象,而这时model压入了值栈,它是处于栈顶的,所以从页面提交过来的参数也就被model对象接收了。
这个ValueStack和 struts的ognl是分不开的。(valueStack是接口,ognl实现了该接口)
ValueStack实际上就是对OGNL的封装,OGNL主要的功能就是赋值与取值,Struts2正是通过ValueStack来进行赋值与取值的!
ValueStack是一个接口,而OgnlValueStack是strtus2中的缺省实现。ValueStack中的数据,分两个部分存放:root和context(这与OGNL中的概念一致),同时ValueStack暴露相关的接口:
void setValue(String expr, Object value);
Object findValue(String expr);
用来通过OGNL表达式对ValueStack中的数据进行操作!
ValueStack中的root对象是CompoundRoot,CompoundRoot继承了ArraryList,提供了额外的方法:push()和pop()方法,用来对root对象中所包含的数据进行存取!
public class CompoundRoot extends ArrayList {
public CompoundRoot() {
}
public CompoundRoot(List list) {
super(list);
}
public CompoundRoot cutStack(int index) {
return new CompoundRoot(subList(index, size()));
}
public Object peek() {
return get(0);
}
public Object pop() {
return remove(0);
}
public void push(Object o) {
add(0, o);
}
}
正是通过这两个方法,CompoundRoot变成了一个栈结构!压栈操作,将导致对象被放到CompoundRoot的第0个元素上(第0个元素 是栈顶),其它对象被依次往后移动;出栈操作,将导致CompoundRoot的第0个元素被移除(即栈顶元素被弹出),其它对象被依次往前移动!
OGNL不支持多个root对象,而struts2能够支持多个root对象,它对OGNL做了扩展。
如果某个OGNL表达式被传递给ValueStack(即调用ValueStack的setValue或findValue方法),而表达式中包含 有对root对象的访问操作,ValueStack将依次从栈顶往栈底搜索CompoundRoot对象中所包含的对象,看哪个对象具有相应的属性,找到 之后,立刻返回。
这里的存是:客户端请求中的参数如何被 “存” 进 ValueStack,例如:添加操作;而 “取” 是指服务器查询数据,如何回显到前台页面,例如 更新操作。
接下来,我们看 存 的实现,其实就是博客开篇提到的,三种方法,一个原则:前台 控件的name必须是和实体属性名称是一致的。这里不再赘述。
主要是看 “取” 的实现。
服务器端,我们需要手动将model 压入 ValueStack:ActionContext.getContext().getValueStack().push(××);
public class UserAction
{
//查看用户的详细信息
public String detail()
{
User u = new User();
u.setUsername("wyx");
ActionContext.getContext().getValueStack().push(u);
return "update";
}
}
在JSP中: 对应的JSP使用 struts2标签 + ognl 来取。
username:<input type="text" name="username" value="<s:property value="username"/>"> <br/>
或者:
<s:textfield name="username"/>
所以struts2的modelDriven机制:
1、客户端请求:当一个请求过来,首先被modelDriveInterceptor拦截,执行getModel方法,压入valueStack,ValueStack 将请求的值和压入的model进行对比赋值;
2、服务端回发:我们需要把model push进ValueStack。前台使用 struts2标签 + ognl来接受,解析。