一.概述
通过之前的博客,我们大致了解了OGNL的语法格式和Struts接收参数的三种方式。
OGNL语法博客地址(点击打开链接),struts2接受参数三种方式的语法(点击打开链接)。
本文将继续介绍OGNL是怎么运用到Struts2中,用于参数接接收的。
二.一般的OGNL对象结构
我们知道OGNL表达式主要包含两部分:root对象和context对象,root对象是OGNL表达式的操作对象,而context对象则代表了root对象
所在的上下文环境。而且root中可以保存任何的对象,而context中则以键值对的方式保存了servlet的十一大内置对象。用图表示如下。
三.struts2中的OGNL结构
struts2在设计之初就借鉴了OGNL语法,并实现了一个很好的OGNL结构。从上,我们知道,OGNL语法要想运行,就必须准备
一个OGNLContext,struts2实现了这样一个OGNLContext,并取名为ValueStack(值栈),与OGNLContextl类似,ValueStack也由两
部分构成,一部分是CompoundRoot,本身是一个底层由list实现的栈内存结构,这个栈里面放置了要访问的action实例。另一部分是
ActionContext,是一个map结构,里面放置了一些参数域对象。struts2对OGNL最重要的改进是Root部分用自定义的
CompoundRoot代替,使用OGNLValueStack的findValue方法可以实现在CompoundRoot中从栈顶到栈底 查找对象的属性值。
以上描述用图表示,大致如下。
四.strut2接收参数的原理解析
阅读此内容必定要先明白struts2接受参数的三种方式:属性驱动,对象驱动,模型驱动。可以参见上文链接。
1.方式一,属性驱动原理解析
我们假设从前端表单页面传来的参数为”name=tom”,接着,此带着参数的请求会经过strut2框架中的拦截器群,其中params
拦截器会拦截此参数请求,并把此参数交由OGNL处理,OGNL拿到此参数,就开始从Root中搜索是否有name属性,一旦发现
存在此参数,就把其赋值为tom.至此,struts2属性驱动接受参数模式就完成全部工作。这样就可以清楚的解释,
为什么要把表单的项设置成action的属性才可接受对象。可用下图帮助理解。
方式二,对象驱动原理解析
同属性驱动类似,当表单传来user.name=tom(此种表单参数的写法是OGNL表达式要求的,详情可参见上文链接的
参数获得方法详解),OGNL会从栈顶对象中(当前访问的action)查找user对象(同样,要把user作为action的对象),
然后再把user的name属性赋值为tom.可以看下图帮助理解。
方式三,模型驱动原理解析
1.自定义实现
现在我们知道,前面两种方式接受参数的方式都是OGNL先获取栈顶对象,即是要访问的action实例,在把值赋给此action
实例的属性即可。但我们知道,模型驱动方式接受参数并没有在action添加额外的属性。既然如此,那模型驱动方式是如何
获取到表单参数的呢。一种很好的思路就是:在参数赋值之前,把接受参数的对象压入栈顶即可。基于此思路,我们可以
自己简单实现一个吗,模型驱动接受参数,特别强调,必须在参数赋值之前就要把对象参数压入栈顶。
我们查找struts2的默认核心配置文件可知,在params拦截器之前,先经过了prepare拦截器,在prepare拦截器中会检查action
是否实现了Prepareable接口,若实现了此接口,则调用prepare方式对action进行必要初始化。在此拦截器被调用时把对象
压入栈顶即可。依然用name=tom为例。在action中可以做如下调整。
public class DemoAction extend ActionSupport implements Preparable//需要实现preparable接口
{
private User user;
execute方法....
@Override
public void prepare()throw Exception
{//在此接口的prepare方法中把对象压入栈顶,此时请求还没经过params拦截器,经过此操作后,位于ValueStck
ValueStack vs=ActionContext.getContext.getValueStack;
vs.push(user);
}
在action中,实现Preparable接口,并在prepare方法中把接受参数的对象压入栈顶就可实现模型驱动方式接受参数。
2.一般方式
值得注意的是strut2实现模型属性驱动的方式与上述自定义的思路基本一致,即在赋值之前把对象压入栈顶。不过struts2在
modelDriven拦截器中实现这种操作,而modelDriven拦截器仍然在params拦截器之前。modelDriven拦截器相关代码如下。
从此代码中看出,大概流程如下,先判断出action实现了ModelDriven->获取值栈->调用getModel方法获取model->把model
压到栈顶。这就清楚解释了模型驱动为什么要求action实现ModelDriven接口,并在getModel方法中返回获取参数的对象。
五.总结
此博客详细记录了struts2三种接受参数写法的原因。通过了解此原理,可以得记忆struts2获取参数的几种方式的写法。