OGNL表达式

什么是OGNL

OGNL表达式是Object-Graph Navigation Language的缩写,是一种功能强大的表达式语言,通过简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转换。在Struts1中,习惯使用的表达式语言是EL,在WebWork2和Struts2.x中使用OGNL来做页面数据绑定。利用表达式,可以直接利用对象曾的对象,更面向对象的操作使得项目不需要封装太多的FormBean。

OGNL取值范围

OGNL表达式是围绕OGNL上下文对象(即OgnlContext)对象进行的,OGNLContext对象分为Root跟Context两部分,Root可以存放任何对象,而Context中只能存放Map,而OGNL表达式会去这两部分取值

OGNL使用

导包

由于struts2的包中已经包含了OGNL的包,因此不需要导入额外的包

准备OGNL对象

// 准备Root
User rootUser = new User("Scarlett", 33);
// 准备Context
Map<String, User> map = new HashMap<>();
map.put("user1", new User("Mike", 15));
map.put("user2", new User("Dick", 17));
		
OgnlContext oc = new OgnlContext();
oc.setRoot(rootUser);
oc.setValues(map);

 书写OGNL表达式

首先准备一个User类,如下

package com.jinxin.ognl;

public class User {
	private String name;
	private Integer age;
	
	public User(String name, Integer age) {
		this.name = name;
		this.age = age;
	}
	
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
}

取值

// 从Root中获取
String name = (String) Ognl.getValue("name", oc, oc.getRoot());
System.out.println(name);
// 从Context中获取
Integer name2 = (Integer) Ognl.getValue("#user1.age", oc, oc.getRoot());
System.out.println(name2);

赋值

// 给Root中的对象的name属性复赋值
String newName = (String) Ognl.getValue("name='Taylor', name", oc, oc.getRoot());
System.out.println(newName);
// 给Context中的user2的name属性赋值
Integer newAge = (Integer) Ognl.getValue("#user2.age = 100, #user2.age", oc, oc.getRoot());
System.out.println(newAge);

调用方法

// 调用Root中的对象的方法
String funcName = (String) Ognl.getValue("setName('hh'), getName()", oc, oc.getRoot());
System.out.println(funcName);
// 调用Context对象中的user1对象的方法
Integer funcAge = (Integer) Ognl.getValue("#user1.setAge(1000), #user1.getAge()", oc, oc.getRoot());
System.out.println(funcAge);

 调用静态方法

首先准备一个EClass类,如下

package com.jinxin.ognl;

public class EClass {
	public static String echo(String param) {
		return param;
	}
}

 使用 完整类名@方法 的形式调用

String echo = (String) Ognl.getValue("@com.jinxin.ognl.EClass@echo('hello world')", oc, oc.getRoot());
System.out.println(echo);

创建LIst对象

// 使用size()方法验证这是一个List
Integer size = (Integer) Ognl.getValue("{'Scarlett', 'Taylor', 'Eric'}.size()", oc, oc.getRoot());

// 可以使用 List[index] 的形式取值
Integer size = (Integer) Ognl.getValue("{'Scarlett', 'Taylor', 'Eric'}[0]", oc, oc.getRoot());      

 创建Map对象

Integer size = (Integer) Ognl.getValue("#{'name':'tom', 'age':'age'}.size()", oc, oc.getRoot());
// 使用 map[key] 的形式取值
String name = (String) Ognl.getValue("#{'name':'tom', 'age':'age'}['name']", oc, oc.getRoot()); 

OGNL表达式与Struts2结合

 

首先介绍一个对象,叫做值栈(ValueStack):

    OGNL的核心就是OGNL上下文对象,即OgnlContext对象,而Struts2所提供的OgnlContext对象就是ValueStack对象,因此ValueStack也分为两部分,其中Root为栈结构,放置的是当前的Action对象,而Context对象存放的是ActionContext数据中心

Root栈结构的实现

上面提到值栈中的Root是一个栈结构,那么这个栈结构是如何实现的呢?要弄清楚这个问题首先得知道栈的特点,同队列的先进先出不同,栈结构是先入后出,就好像子弹压入弹夹一样,最先压进去的子弹在弹夹的最下方,反而是最后打出去的,那么知道这个特点后就可以着手实现这样一个栈结构了,这里选用List集合来实现,根据栈的特点,在压栈的时候把最新放入的数据始终存到集合的0号索引处 list.add(0, Obj) ,其他的值依次往后推,在弹栈的时候讲0号索引处的数据弹出 list.remove(0) ,后面的数据依次往前补

Struts2与ognl结合的体现

接收参数

前面有讲过struts2获取前端表单的参数的方法有三种,分别是 属性驱动,对象驱动,模型驱动,这些很“神奇”的方式都是通过ognl实现的

属性驱动如图

对象驱动如图

模型驱动如图

实现模型驱动的方法很简单,就是在参数赋值前将User对象压入栈顶即可。

获取值栈,并压栈

ValueStack v = ActionContext.getContext().getValueStack();
vs.push(user);

 不过不能在Action中压,因为在请求到达Action之前会经过20个拦截器,赋值的操作在哪里就已经完成了,准确的说是在params拦截器中就已经赋值了,然后再到Action中讲User对象压入栈中还有什么意义呢?所以应该在params拦截器之前的拦截器中完成压栈操作

 

其中在一个叫做prepare的拦截器在params拦截器之前执行,在该拦截器中会执行一个prepare()方法,那么就可以在这个方法中将User压入栈中,之后在params拦截器中赋值就能找到User对象了。

那么要执行上述的操作必须先让当前的Action实现一个Preparable的接口,然后再实现prepare()方法,具体实现如下

public class DemoAction extends ActionSupport implements Preparable{
    private User user = new User();

    @override
    public void prepare() throws Exception{
        // 获取ValueStack
        ValueStack v = ActionContext.getContext().getValueStack();
        // 将user对象压入栈中
        vs.push(user);
    }
}

 除了上面的方法以外,还有一种方法,在做模型驱动获取参数的时候也曾经实现过一个接口,叫做ModelDriven,在拦截器中有一个modelDriven,也在params拦截器之前执行,那么跟上面一样,同样可以将压栈的操作放到这个拦截器中执行。同样是在Avtion类中实现ModelDriven接口,然后实现 getModel() 方法,只是不再需要在getModel()方法中自己将user压进栈顶了,只需要讲user对象返回即可,在拦截器中接收到了这个对象就会执行压栈操作了,具体代码如下

public class DemoAction extends ActionSupport implements ModelDriven<User>{
    private User user = new User();

    public User getModel(){
        return user;    // 将user返回即可
    }
} 

 上述操作已经将user对象返回了,那么在拦截器中接收到后压入栈顶即可,如图

上图的 Object model = modelDriven.getModel(); 一行便是获取了user对象,然后再执行 stack.push(model); 将user压入栈顶

在配置文件中

有的时候在转发或者重定向到Action中的时候希望携带参数过去,这时也可以用到ognl表达式

<param name="actionName">Demo01Action</param>
<param name="namespace">/demo</param>
<param name="name">${name}</param>   // 这里是ognl表达式,从栈顶寻找当前Action的name属性

 

posted @ 2018-07-16 16:24  Jin同学  阅读(1399)  评论(0)    收藏  举报