文章目录
- OGNL 是干什么用的
- 示例代码一
- 示例代码二
- 使用OGNL获取JavaBean对象的属性值
- 获取集合属性中元素的属性的值
- XWork 中对 OGNL 的扩展
- 示例代码
- Struts2 对 OGNL 的封装
- OGNL 可以用在哪些地方
- OGNL 的结构示意图
- XWork 对 OGNL 改造后的结构示意图
- Struts 2 对 OGNL 改造后的结构示意图
- OGNL 如何将请求参数的值赋给 Action 对象的属性?
- OGNL 如何获取属性值?
- 参考
OGNL 是干什么用的
Object Graph Navigation Language,中文名叫对象导航图语言,缩写为 OGNL,类似于 EL 表达式,是表达式语言中的一种。
标准的 OGNL 涉及到 3 个概念:OGNL 引擎、JavaBean 对象、Map 对象。
JavaBean 对象:OGNL 操作的对象,这些对象中含有 setter/getter 方法。通常取名为 root。
Map 对象:用于存放和整个系统有关的公共数据,通常取名为 context。
当有了 OGNL 引擎,我们就可以访问各种各样的 root 对象(比如 Foo 对象、Emp 对象、Dept 对象等),在访问中有一些数据是每一次访问都需要用到的,这些数据就可以保存在 context 对象中。
ognl.jar 工具包提供了一个 OGNL 引擎,这个引擎会解析 OGNL 字符串表达式,从而让 OGNL 引擎去读取和设置对象的属性。
把 Java 对象看成一张地图,如果这个 Java 对象的属性很多而且层次复杂,例如属性值是对象,这个对象又是集合,集合中又都是集合,里面的集合中的元素是对象,对象又有属性,属性值又是对象,这样的结构相当复杂,如果你要去访问其中的属性值,OGNL 可以提供类似地图导航仪的功能帮助我们找到指定的属性值或者方法。
支持类的静态方法调用和静态变量的访问。表达式的格式为:@[类全名(包括包路径)]@[方法名 | 值名]
。
例如:
调用类的静态方法:
@java.lang.String@format('foo %s', 'bar')
访问类的静态变量:
@tutorial.MyConstant@APP_NAME;
示例代码一
Bar:
package priv.lwx.jstl.bean;/*** description** @author liaowenxiong* @date 2022/2/12 15:44*/public class Bar {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}
BarTest:
package priv.lwx.jstl.bean;import ognl.Ognl;
import ognl.OgnlException;
import org.junit.jupiter.api.Test;import java.util.HashMap;
import java.util.Map;/*** description** @author liaowenxiong* @date 2022/2/12 15:45*/public class BarTest {@Testpublic void test() throws OgnlException {// 自定义一个context对象Map ctx = new HashMap<>();ctx.put("num", 10);// root对象Bar root = new Bar();root.setName("bar");System.out.println(Ognl.getValue("name", root));//不加"#",表示从业务对象root中取数据System.out.println(Ognl.getValue("name", ctx, root));//加"#",表示从公共对象context中取数据System.out.println(Ognl.getValue("#num", ctx, root));}
}
示例代码二
使用OGNL获取JavaBean对象的属性值
Foo:
package priv.lwx.jstl.bean;import java.util.List;
import java.util.Map;/*** description** @author liaowenxiong* @date 2022/2/11 22:10*/public class Foo {private Integer id;private String name;private String[] array;private List<String> list;private Map<String, String> map;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String[] getArray() {return array;}public void setArray(String[] array) {this.array = array;}public List<String> getList() {return list;}public void setList(List<String> list) {this.list = list;}public Map<String, String> getMap() {return map;}public void setMap(Map<String, String> map) {this.map = map;}
}
FooTest:
package priv.lwx.jstl.bean;import ognl.Ognl;
import org.junit.jupiter.api.Test;import java.util.Arrays;
import java.util.HashMap;/*** 使用OGNL获取JavaBean对象的属性值** @author liaowenxiong* @date 2022/2/11 22:13*/public class FooTest {@Testpublic void test() throws Exception {Foo foo = new Foo();foo.setId(100);foo.setName("Java");foo.setArray(new String[]{"one", "two", "three"});foo.setList(Arrays.asList("A", "B", "C"));HashMap<String, String> map = new HashMap<>();map.put("one", "Java");map.put("two", "JavaJava");map.put("three", "JavaJavaJava");foo.setMap(map);/*** Ognl引擎访问对象的格式:* Ognl.getValue("OGNL表达式", root对象); root对象是Ognl要操作的对象**/System.out.println(Ognl.getValue("id", foo));System.out.println(Ognl.getValue("name", foo));// 获取数组、集合中指定下标值的元素(root对象的数组和集合属性)System.out.println(Ognl.getValue("array[1]", foo));System.out.println(Ognl.getValue("list[1]", foo));// 获取Map集合中的数据(root对象的Map属性)System.out.println(Ognl.getValue("map.one", foo));System.out.println(Ognl.getValue("map['two']", foo));// 基本运算System.out.println(Ognl.getValue("id+100", foo));// System.out.println(Ognl.getValue("\"What is \"+name", foo));// System.out.println(Ognl.getValue("\'What is \'+name", foo));System.out.println(Ognl.getValue("'What is '+name", foo));System.out.println(Ognl.getValue("'name:'+name+' id:'+id", foo));System.out.println(Ognl.getValue("id>50", foo));// 调用方法System.out.println(Ognl.getValue("name.toUpperCase()", foo));System.out.println(Ognl.getValue("list.size()", foo));// 方法的参数也可以使用属性System.out.println(Ognl.getValue("map.three.lastIndexOf(name)", foo));// 调用静态方法,以取出的属性值作为参数System.out.println(Ognl.getValue("@java.util.Arrays@toString(array)", foo));// Ognl中只能创建List对象和Map对象// 创建List对象Object obj = Ognl.getValue("{1,2,3}", null);System.out.println(obj.getClass().getName());System.out.println(obj);// 创建Map对象obj = Ognl.getValue("#{'one':'Java','two':'JavaJava'}", null);System.out.println(obj.getClass().getName());System.out.println(obj);}
}
获取集合属性中元素的属性的值
Emp:
package priv.lwx.jstl.bean;import java.math.BigDecimal;
import java.util.Date;/*** description** @author liaowenxiong* @date 2022/2/12 13:40*/public class Emp {private Integer id;private String name;private BigDecimal salary;private Date hireDate;public Emp() {}public Emp(Integer id, String name, BigDecimal salary, Date hireDate) {this.id = id;this.name = name;this.salary = salary;this.hireDate = hireDate;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public BigDecimal getSalary() {return salary;}public void setSalary(BigDecimal salary) {this.salary = salary;}public Date getHireDate() {return hireDate;}public void setHireDate(Date hireDate) {this.hireDate = hireDate;}
}
Dept:
package priv.lwx.jstl.bean;import java.util.List;/*** description** @author liaowenxiong* @date 2022/2/12 13:39*/public class Dept {private Integer id;private String name;private List<Emp> empList;public Dept() {}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public List<Emp> getEmpList() {return empList;}public void setEmpList(List<Emp> empList) {this.empList = empList;}
}
DeptTest:
package priv.lwx.jstl.bean;import ognl.Ognl;
import ognl.OgnlException;
import org.junit.jupiter.api.Test;import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;/*** 获取集合属性中元素的属性的值** @author liaowenxiong* @date 2022/2/12 14:49*/public class DeptTest {@Testpublic void test() throws OgnlException {Dept dept = new Dept();List<Emp> emps = new ArrayList<>();emps.add(new Emp(100, "emp1", new BigDecimal(10000), new Date()));emps.add(new Emp(200, "emp2", new BigDecimal(15000), new Date()));emps.add(new Emp(300, "emp3", new BigDecimal(12000), new Date()));dept.setEmpList(emps);dept.setName("dept1");// 获取集合属性中元素的属性的值String name = (String) Ognl.getValue("empList[0].name", dept);BigDecimal salary = (BigDecimal) Ognl.getValue("empList[0].salary", dept);System.out.println("name:" + name + "," + "salary:" + salary);/*** list.{attr}表示把list中的每一个元素的attr属性值取出,组合为一个ArrayList,并返回*/Object obj = Ognl.getValue("empList.{salary}", dept);System.out.println(obj.getClass().getName());System.out.println(obj);// 过滤(不常用,理解即可)// 找出薪水大于12000的员工姓名obj = Ognl.getValue("empList.{?#this.salary >= 12000}.{name}", dept);System.out.println(obj.getClass().getName());System.out.println(obj);}
}
XWork 中对 OGNL 的扩展
上面关于 OGNL 的应用是标准的、通用的用法。而在 XWork 中,OGNL 操作的对象不再是简单的 JavaBean 对象,而是一个 CompoundRoot 对象,而这个 CompoundRoot 实际上是继承自 CopyOnWriteArrayList,而 CopyOnWriteArrayList 实现自 List,所以 CompoundRoot 对象其实就是集合。
CompoundRoot 的数据存取机制类似“栈”,即后进先出,先进后出。在这个对象中可以存放多个 JavaBean 对象,如果 OGNL 表达式是 “name”, 会从 CompoundRoot 的栈顶开始依次查找含有 name 属性的对象,找到了就不再继续查找,而是调该对象的 getName() 方法获取属性 name 的值。
示例代码
Bar:
package priv.lwx.struts2.ognl.entity;/*** description** @author liaowenxiong* @date 2022/2/12 16:43*/public class Bar {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}
BarTest:
package priv.lwx.struts2.ognl.entity;import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
import com.opensymphony.xwork2.util.CompoundRoot;
import ognl.Ognl;
import ognl.OgnlException;
import ognl.OgnlRuntime;
import org.junit.Test;/*** description** @author liaowenxiong* @date 2022/2/12 16:44*/public class BarTest {@Testpublic void test() throws OgnlException {// 创建一个CompoundRoot对象CompoundRoot root = new CompoundRoot();Bar bar1 = new Bar();bar1.setName("bar1");root.push(bar1);Bar bar2 = new Bar();bar2.setName("bar2");root.push(bar2);// 定制OGNL的Root机制为CompoundRoot机制OgnlRuntime.setPropertyAccessor(CompoundRoot.class,new CompoundRootAccessor());String name = (String) Ognl.getValue("name", root);System.out.println(name);}
}
Struts2 对 OGNL 的封装
Struts2 是在 XWork 的基础上进行扩展得到的框架,而 Struts2 在 XWork 对 OGNL 进行扩展的基础上又进行了扩展。将 CompoundRoot 对象和 Map 对象封装到 ValueStack 对象中。
Struts2 在请求到来时,首先会创建一个 ValueStack,接着再创建 Action 对象。Struts2 会把当前被请求的 Action 放入 CompoundRoot 对象的栈顶,请求结束后,Action 对象会被清除掉。并把内部创建的 PageContext、Request、Session、ServletContext 等对象的引用地址全部存放在 context 对象中。
Struts2 会把 ValueStack 存放在 Request 中,属性名称为 struts.valueStack
。
我们可以通过 OGNL 表达式访问 CompoundRoot 对象中的 Action 对象。
可以在 JSP 文件中使用 Struts2 的 <s:debug/>
标签来查看 ValueStack 的内容:
<%@ page import="java.util.*" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="/struts-tags" prefix="s" %>
<html>
<head><title>Title</title>
</head>
<body>
<%--s:debug标签用于看valueStack,用于调试--%>
<s:debug/>
</body>
</html>
OGNL 可以用在哪些地方
ognl 是一种字符串表达式,在 Java 语境中和 JSP 语境中均可以使用,但是在 JSP 语境中使用,必须结合Struts2 的标签来使用,无法独立使用。
OGNL 的结构示意图
XWork 对 OGNL 改造后的结构示意图
Struts 2 对 OGNL 改造后的结构示意图
ValueStack 有两个属性 root 和 context,root 是 CompoundRoot 实例,而 CompoundRoot 是继承自 CopyOnWriteArrayList 的子类,context 是 ActionContext 的实例,而 ActionContext 是 OgnlContex 的子类,OgnlContext 是 Map 的实现类。
对象 context 中有个属性,名称也叫 root,该属性也引用 CompoundRoot 实例。
请求到达 Struts 2 的控制器时,ValueStack 对象就创建出来了,接着创建 Action 对象,并将 Action 对象存储到 ValueStack 中的成员变量 root 所指向的 CompoundRoot 对象中。
OGNL 如何将请求参数的值赋给 Action 对象的属性?
先从 Request 对象获取请求参数的值,然后执行以下的代码:
Ognl.setValue("name",action,"liaowenxiong")
setValue 方法会查找 action 对象是否存在 setName 方法,存在在调用该方法,并将 liaowenxiong 作为参数传入该方法中,如果不存在则啥也不做。
OGNL 如何获取属性值?
ValueStack vs = ActionInvocation.getStack();
vs.findValue("ognl表达式");
vs.findValue("imageStream");
Object obj = Ognl.getValue("OGNL表达式",root,context);
参考
https://www.cnblogs.com/cenyu/p/6233942.html
https://tech.souyunku.com/?p=23119
https://www.cnblogs.com/dkz1/p/8024675.html