第6章 灵丹妙药 —— OGNL,数据流转的催化剂
6.2 强大的OGNL OGNL (Object Graph Navigation Language) 是一个开源的表达式引擎。通过使用OGNL,我们能够通过表达式存取Java对象树中的任意属性和调用Java对象树的方法等。也就是说,如果我们把表达式看成是一个带有语义的字符串,那么OGNL就是这个语义字符串与Java对象之间沟通的催化剂,通过OGNL,我们可以轻松解决在数据流转的过程中所碰到的各种问题。 6.2.1深入OGNL的API 我们首先用最直观的方式:通过研究OGNL的原生API来看看如何使用OGNL来进行对象的存取操作。首先来看一下来自于OGNL的静态方法,如代码清单6-4所示:- /**
- * 通过传入的OGNL表达式,在给定的上下文环境中,从root对象里取值
- */
- public static Object getValue(String expression, Map context, Object root) throws OgnlException {
- return getValue(expression, context, root, null);
- }
- /**
- * 通过传入的OGNL表达式,在给定的上下文环境中,往root对象里写值
- */
- public static void setValue(String expression, Map context, Object root, Object value) throws OgnlException {
- setValue(parseExpression(expression), context, root, value);
- }
- public class BasicOgnlTest extends TestCase {
- @SuppressWarnings("unchecked")
- public void testGetValue() throws Exception {
- // 创建Root对象
- User user = new User();
- user.setId(1);
- user.setName("downpour");
- // 创建上下文环境
- Map context = new HashMap();
- context.put("introduction","My name is ");
- // 测试从Root对象中进行表达式计算并获取结果
- Object name = Ognl.getValue(Ognl.parseExpression("name"), user);
- assertEquals("downpour",name);
- // 测试从上下文环境中进行表达式计算并获取结果
- Object contextValue = Ognl.getValue(Ognl.parseExpression("#introduction"), context, user);
- assertEquals("My name is ", contextValue);
- // 测试同时从将Root对象和上下文环境作为表达式的一部分进行计算
- Object hello = Ognl.getValue(Ognl.parseExpression("#introduction + name"), context, user);
- assertEquals("My name is downpour", hello);
- }
- public void testSetValue() throws Exception {
- // 创建Root对象
- User user = new User();
- user.setId(1);
- user.setName("downpour");
- // 对Root对象进行写值操作
- Ognl.setValue("group.name", user, "dev");
- Ognl.setValue("age", user, "18");
- assertEquals("dev", user.getGroup().getName());
- }
- }
- // 获取Root对象中的name属性的值
- name
- // 获取Root对象中department属性中的name属性的实际值
- department.name
- // 获取Root对象中department属性中manager属性中name属性的实际值
- department.manager.name
6.2.3.2 对上下文环境(Context)的访问 由于OGNL的上下文是一个Map结构,在OGNL进行计算时可以事先在上下文环境中设置一些参数,并让OGNL将这些参数带入进行计算。有时候也需要对这些上下文环境中的参数进行访问,访问这些参数时,需要通过#符号加上链式表达式来进行,从而表示与访问Root对象(Root Object)的区别。
- // 获取OGNL上下文环境中名为introduction的对象的值
- #introduction
- // 获取OGNL上下文环境中名为parameters的对象中user对象中名为name的属性的值
- #parameters.user.name
- // 访问com.example.core.Resource 类中名为ENABLE的属性值
- @com.example.core.Resource@ENABLE
- // 调用com.example.core.Resource 类中名为get的方法@com.example.core.Resource@get()
- // 调用Root对象中的group属性中users的size()方法
- group.users.size()
- // 调用Root对象中的group中的containsUser的方法,并将上下文环境中名为requestUser的值作为参数传入
- group.containsUser(#requestUser)
- 2+4 // 加
- ‘hello’ + ’‘world‘’ // 字符串叠加
- 5-3 // 减
- 9/2 // 除
- 9 mod 2 // 取模
- foo++ // 递增
- foo == bar // 等于判断
- foo in list // 是否在容器中
- // 访问Root对象中的group属性中users中第一个对象的name属性值
- group.users[0].name
- // 访问OGNL上下文中名为sessionMap的Map对象中key为currentLogonUser的值
- #sessionMap['currentLogonUser']
- ? 选择满足条件的所有元素
- ^ 选择满足条件的第一个元素
- $ 选择满足条件的最后一个元素
- // 返回Root对象中的group属性中users这个集合中所有元素的name构成的集合
- group.users.{name} // 新的以name为元素的集合
- // 将group中users这个集合中的元素的code和name用-连接符拼起来构成的字符串集合
- group.users.{code + ‘-’ + name} // 新的以‘code – name’为元素的集合
- // 返回Root对象的group中users这个集合所有元素中name不为null的元素构成的集合
- group.users.{? #this.name != null} // 过滤后的users集合
- 构造List —— 使用{},中间使用逗号隔开元素的方式表达列表
- 构造Map —— 使用#{},中间使用逗号隔开键值对,并使用冒号隔开key和value来构造Map
- 构造对象 —— 直接使用已知对象的构造函数来构造对象
- // 构造一个List
- { "green", "red", "blue"}
- // 构造一个Map
- #{ "key1" : "value1", "key2" : "value2", "key3" : "value3"}
- // 构造一个java.net.URL对象
- new java.net.URL("http://localhost/")
- // 返回group中users这个集合中所有age比3大的元素构成的集合
- users.{? #this.age > 3}
- // 返回group中users这个集合里的大小+1的值
- group.users.size().(#this+1)
- // 返回Root对象的group中users这个集合所有元素中name不为null的元素构成的集合group.users.{? #this.name != null}
- 加在普通OGNL表达式前面,用于访问OGNL上下文中的变量
- 使用#{}语法动态构建Map
- 加在this指针之前表示对this指针的引用
这3种不同的用途在不同的地方有着不同的妙用,尤其是对OGNL上下文中的变量的访问,将成为Struts2在页面级别进行容器变量访问的重要理论基础。