前面几篇笔记中,我们在jsp页面中向Action传递了参数,通过这样三种方式:
1.属性驱动
将参数作为Action的一个属性
2.对象驱动
将实体类作为Action的属性
3.模型驱动
Action实现ModelDriven接口;实体类作为私有变量并实例化;实现getModel()方法,并返回实体对象。
现在探究一下原理:
关于1、2两种方式,我们在上一篇中谈论过,OgnlValueStack的Root中存储有当前action,那么,按照Ognl表达式的语法,”name=’xxx’”的语义即为将xxx赋值给Action的name属性,同理,”user.name=’xxx’”即赋值给Action的user属性的name属性。
但模型驱动的方式其原理则稍微复杂一些。已知的是root是一个栈结构,所以理论上要为user对象赋值,需要将其压入栈中,使其处在栈顶位置。
我们知道在数据提交到Action的过程中将会经过二十个拦截器,这些拦截器中有一个拦截器,负责将数据赋值给对象,该拦截器可以在struts.default.xml中看到,标签为<params>
,而赋值需要在将对象压入值栈之后进行。这时就需要<prepare/>
拦截器发挥作用,该拦截器执行顺序在action.excute之前,用以对action进行准备操作。查看源码,所在包:package com.opensymphony.xwork2.interceptor;
核心源码如下:
public String doIntercept(ActionInvocation invocation) throws Exception {
//获取当前操作的Action
Object action = invocation.getAction();
//判断是否为Prepareable接口实现对象
if (action instanceof Preparable) {
try {
String[] prefixes;
if (this.firstCallPrepareDo) {
prefixes = new String[]{"prepareDo", "prepare"};
} else {
prefixes = new String[]{"prepare", "prepareDo"};
}
//反射方式为其实现该接口
PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes);
} catch (InvocationTargetException var5) {
Throwable cause = var5.getCause();
if (cause instanceof Exception) {
throw (Exception)cause;
}
if (cause instanceof Error) {
throw (Error)cause;
}
throw var5;
}
if (this.alwaysInvokePrepare) {
((Preparable)action).prepare();
}
}
return invocation.invoke();
}
这样,我们就能够在赋值前,将其先进行入栈操作。
那么实现ModelDriven接口就可以为属性赋值就变得很好解释了,在struts-default.xml中,我们可以看到modelDriven这个拦截器的顺序是在param之前的,而在其源码之中,还能看到它将action中的对象压入了值栈。