一.ognl表达式
1.ognl表达式跟el表达式都是对象视图导航语言,不过ognl比el在功能上更加强大,除了输出外,还可以为对象赋值,调用对象方法,调用静态方法等
2.在EL中我们取值是从11个内置对象中获取,而我们的ognl是从ognlContext中来获取。
3.ognlContext实质是一个map,这个map还分为两部分,root部分和context部分,root部分中可以任意类型的对象作为root对象,且root对象只能有一个;context部分说白也是map,你调用ognlContext对象的put存储键值对,其实是属于放在了context中,主要是为了区分root和context
在下述操作前,先做一些对象准备工作,如图:
二.如何从ognlContext对象中取值?
1.可以调用Ognl.getValue(expression, ognlContext, root),参数一是表达式,参数二是ognlContext对象,参数三是ognlContext对象中的root对象
2.如果想要获取root对象的属性,直接在表达式中写root对象的属性名,如:Ognl.getValue("name", ognlContext, root);
3.如果想要获取context中的值,得再表达式的前面加一个#,如:Ognl.getValue("#key.name", ognlContext, root);key为键值对的键,#key就得到了值;
三.ognl如何设置属性值?
1.给root对象设置属性值,表达式格式是"属性名=修改后的值",如:Ognl.getValue("name='王五'", ognlContext, root);
2.给context中键值对的值对象设置属性,如:Ognl.getValue("#key.name='多佛朗', ognlContext, root);
四.ognl如何调用方法?
1.调用root对象的方法,很简单,表达式直接写方法名即可,如:Ognl.getValue("getName()", ognlContext, root);如果调用的方法有返回值,则会有返回值返回
2.context中调用值对象的方法,如:Ognl.getValue("#key.setName('大帅哥')", ognlContext, root);
五.ognl如何调用静态方法?
1.调用静态方法,得在类名和调用的方法名前加@,结构为:@完整类名@方法名,如:Ognl.getValue("@java.lang.Math@PI", ognlContext, u1);
六.ognl创建List和map
1.创建list的话,表达式中加{ },代表要创建list,如:
Ognl.getValue("{'tom','jerry','jeck'}", ognlContext, root);创建的list是不会放到ognlContext去的,map也如此
Ognl.getValue("{'tom','jerry','jeck'}[1]", ognlContext, root);获取指定索引的值
gnl.getValue("{'tom','jerry','jeck'}.get(2)", ognlContext, root);获取指定索引的值
2.创建map,表达式中加#{ },代表要创建map,如:
Ognl.getValue("#{'tom':'18','jerry':'12','jeck':'10'}", ognlContext, root);
Ognl.getValue("#{'tom':'18','jerry':'12','jeck':'10'}['jerry']", ognlContext, root);获取指定键的值
Ognl.getValue("#{'tom':'18','jerry':'12','jeck':'10'}.get('tom')", ognlContext, root);获取指定键的值
七.ognl与struts2的结合
1.ognl其实跟struts2在技术没什么联系,不过struts2把ognl纳入进来使用而已
2.OgnlContext对象在struts2中不叫OgnlContext了,叫值栈ValueStack;不过里面的分类还是跟之前一样,分root部分和context部分
3.struts2已经在ValueStack已经为我们预置了要使用的东西,在root部分放置了一个List栈,在context部分放置了一个ActionContetx对象,ValueStack是一个接口,而我们去查看它的实现类OgnlValueStack源码,有两个变量,如图:
root就是我们的list栈,而context就是我们的ActionContext
4.比如说在List栈中,有几个user对象,user类有name属性,我们要从栈中获取name属性值时,是从栈顶的对象开始找的,如果找不到继续往下找
5.ActionContext中引用了很多servlet原生api的东西,在我们的List栈中,默认情况下,放置的是当前访问的Action对象
八.ognl与struts2的结合之参数接收
1.记得在Action类中接收参数的三种方式吗?分别是属性驱动,对象驱动,模型驱动;
2.这三种驱动的实现就是依赖我们的ognl来做的;
(1)属性驱动;比如:表单页面提交name="username"该键值对参数到我们的Action之前,会经历20个拦截器,其中有一个params拦截器会调用ognl,ognl会解释该键值对为符合ognl表达式的语法,从我们的valueStack中,root部分的list栈中赋值,假设我们的Action对象就放置在栈顶,它会从栈顶的Action对象中查看有没有username属性,如果有给其赋值。
(2)对象驱动;比如:表单页面提交name="user.username"该键值对参数到我们的Action之前,会经历20个拦截器,其中有一个params拦截器会调用ognl,ognl会解释该键值对为符合ognl表达式的语法,从我们的valueStack中,root部分的list栈中赋值,又假设我们的Action对象就放置在栈顶,它会从栈顶的Action对象中查看有没有user对象的属性,如果有给user对象的username赋值。
(3)模型驱动;这个相对前面两个就有意思了,要是按照上面两个的逻辑的话,表单页面提交name="username",而我们的Action中是user成员变量(不是属性),好像解释成ognl表达式后直接放的话是不放进去的吧;
先不谈模型驱动的接口,那如何才能让name="username"放到user对象中?我们是不是可以把Action对象中的user对象在list栈中压栈到栈顶吧,这样的话就可以根据ognl表达式赋进去了吧;
那问题也来了,什么时候把user压栈?因为params拦截器调用ognl到valueStack进行的赋值,如果我们在它之后进行压栈的话就已经晚了,所以我们得在此之前进行压栈的操作,可以考虑在params前面的拦截器进行操作,手动实现一个Preparable接口,重写它的prepare方法,在该方法里面进行压栈即可;
为什么可以这样做呢,是因为在params拦截器前有一个prepare拦截器,该拦截器会判断你的action是否实现了Preparable接口,若是的话会调用重写的prepare方法。
但是呢struts2觉得这样对开发者麻烦,直接让实现ModelDriven接口,重写getModel()把你要压栈的对象传给它即可,后续的操作人家modelDriven拦截器会帮你完成,原理跟上面的是一样的。
PS:想要获取ValueStack,可以通过ActionContext来获取,他们两个是互相引用的
九.ognl与struts2的结合之配置文件
1.如果Action方法的返回值是需要重定向Action且需要带参数,配置代码可以这样写:
因为ognl会从值栈来取,开头没有#,会取list栈的栈顶对象取值,我们可以把要传的参数设置成Aciton对象的成员属性即可