(九)Struts2进阶之OGNL表达式第一弹

这两天一直想写OGNL的总结,但发现下不了笔。今天还是咬牙开始写。

OGNL是Object-Graph Navigation Language的缩写,它是一种功能强大的表达式语言,通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。 ------百度百科

OGNL我们看到最多的就是和Struts2的标签结合使用,但其实OGNL离开了Struts2也是可以的,只是用在Struts2中,就必须和标签库结合才能使用。

这篇文章就先讲讲OGNL不结合Struts2的一些用法,下篇文章再讲OGNL在Struts2中的用法。

1.使用OGNL前的准备工作

要使用OGNL,得导入相应的jar包。


需要带的jar包

2.OgnlContext类和Ognl类的介绍

在类中使用OGNL表达式,和两个类息息相关,分别是OgnlContext类和Ognl类。

Ognl类:This class provides static methods for parsing and interpreting OGNL expressions.根据官方解释,这个类是提供一些静态方法去解析表达式。

OgnlContext:This class defines the execution context for an OGNL expression.该类定义OGNL表达式的执行上下文。

public class OgnlContext extends Object implements Map {}

OgnlContext类实现了Map接口,所以它也是一个Map,可以通过put方法往该上下文环境中放元素。该上下文环境中,有两种对象:根对象和普通对象。我们可以使用它的setRoot方法设置根对象。根对象只能有一个,而普通对象则可以有多个。

3.Ognl获取普通对象和根对象的方法

在上下文环境中,有根对象和普通的对象,两者的获取方式有所不同:获取根对象的属性值,可以直接使用属性名作为表达式,也可以使用#对象名.属性名的方式;获取普通对象的属性值,则必须使用#对象名.属性名的方式获取。下面举例说明。

新建一个School类,有学校名称和老师的集合两个属性

package com.codeliu.ognl;

import java.util.List;

public class School {
    private String name;
    private List teachers;
    
    public School() {}
    
    public School(String name, List teachers) {
        super();
        this.name = name;
        this.teachers = teachers;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public List getTeachers() {
        return teachers;
    }
    public void setTeachers(List teachers) {
        this.teachers = teachers;
    }
    
}

新建一个Teacher类,有姓名、年龄、性别三个属性

package com.codeliu.ognl;

public class Teacher {
    private String name;
    private String gender;
    private int age;
    public Teacher(String name, String gender, int age) {
        super();
        this.name = name;
        this.gender = gender;
        this.age = age;
    }
    public Teacher() {}
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    
}

新建一个测试类Test

public class Test {
    public static void main(String[] args) throws OgnlException {
        // 定义一个老师对象
        Teacher t1 = new Teacher("CodeTiger", "男", 22);
        List lists = new ArrayList();
        lists.add(t1);
        // 定义一个学校
        School s = new School("NJUPT", lists);
        // 创建一个OgnlContext实例对象
        OgnlContext context = new OgnlContext();
        // 把学校和老师放入上下文环境中
        context.put("t1", t1);
        context.put("s", s);
        // 设置学校为根对象
        context.setRoot(s);
        // 会覆盖前面的根对象
        // context.setRoot(t1);
        
        // 解析表达式
        // Object expression = Ognl.parseExpression("#s.name");
        Object expression = Ognl.parseExpression("name");
        // Object expression = Ognl.parseExpression("s.name"); // 出错
        // 直接获取根对象的信息,使用#获取上下文中的属性值时,必须使用带Map context参数getValue方法,指定上下文环境
        // 如果是获取根对象,没有使用#,则可以使用不带Map context参数getValue方法,我们也可以使用#获取根对象属性值
        Object result = Ognl.getValue(expression, context, context.getRoot());
        System.out.println(result);
        
        // 获取普通对象的属性
        expression = Ognl.parseExpression("#t1.gender");
        // 此时必须指定上下文环境,不然找不到
        result = Ognl.getValue(expression, context, context.getRoot());
        System.out.println(result);
        
        // 通过根对象获取普通对象的属性
        // expression = Ognl.parseExpression("teachers[0].name");
        expression = Ognl.parseExpression("#s.teachers[0].name");
        // expression = Ognl.parseExpression("s.teachers[0].name"); // Exception in thread "main" ognl.NoSuchPropertyException: com.codeliu.ognl.School.s
        result = Ognl.getValue(expression, context, context.getRoot());
        System.out.println(result);
    }
}

运行输出

NJUPT
男
CodeTiger

以上有几个地方要注意:
1.根对象只能有一个,当你尝试设置多个的时候,后面的会覆盖前面的。
2.访问根对象中的属性,可以直接使用属性名,或者#对象名.属性名,但不能使用对象名.属性名,不然它会认为这是根对象中的一个属性叫对象名,然后访问这个属性的名叫属性名的属性值。(说的我都懵了)
比如我上面获取学习的名称,使用下面的表达式

Object expression = Ognl.parseExpression("s.name"); // 出错

则会出现下面的错误

Exception in thread "main" ognl.NoSuchPropertyException: com.codeliu.ognl.School.s

提示你找不到School类中的s属性,凭你debug多年的经验,应该一眼就能看出问题所在。
3.不使用#获取根对象中的属性值时, 使用getValue方法可以不指定上下文环境,因为第三个参数已经得到了根对象,它指定去根对象中找。如果获取普通对象的属性,则必须在getValue方法中指定上下文环境,因为你不知道它找不到。
4.通过根对象间接获取普通对象的属性时,比如List集合,使用下标去获取。下面会详细讲。

4.Ognl访问非静态方法、静态方法、静态字段的方法

Ognl不但可以访问我们自己定义的类的属性和方法,还可以访问Java API中的静态方法和静态字段。看下面的代码

        // 直接调用根对象中School的getName方 法获取学校名
        // expression = Ognl.parseExpression("getName()");
        expression = Ognl.parseExpression("#s.getName()");
        result = Ognl.getValue(expression, context, context.getRoot());
        System.out.println(result);
        
        // 调用普通对象中的非静态方法
        // expression = Ognl.parseExpression("#t1.getName()");
        expression = Ognl.parseExpression("#t1.getName().length()");
        result = Ognl.getValue(expression, context, context.getRoot());
        System.out.println(result);
        
        // 调用根对象中的静态方法
        expression = Ognl.parseExpression("getTeacherName()");
        result = Ognl.getValue(expression, context, context.getRoot());
        System.out.println(result);
        // 调用API中的静态方法
        expression = Ognl.parseExpression("@java.lang.Math@floor(4.5)");
        result = Ognl.getValue(expression, context, context.getRoot());
        System.out.println(result);
        
        // 调用API中的静态属性
        expression = Ognl.parseExpression("@java.lang.Math@PI");
        result = Ognl.getValue(expression, context, context.getRoot());
        System.out.println(result);

我特地在School类中增加了一个静态方法获取老师集合中第一个老师的姓名,然后使用Ognl调用它。

输出结果如下

NJUPT
9
CodeTiger
4.0
3.141592653589793

5.Ognl获取数组、集合、Map中元素的方法

Ognl表达式可以创建实例对象,以及获取实例对象中的属性,下面我们看看怎么通过Ognl创建一个集合、map以及通过Ognl获取里面的元素。

public class Test2 {
    public static void main(String[] args) throws OgnlException {
        OgnlContext context = new OgnlContext();
        // 通过Ognl可以创建java的实例对象,只有是类的完整路径
        Object expression = Ognl.parseExpression("new java.util.ArrayList()");
        Object result = Ognl.getValue(expression, context, context.getRoot());
        System.out.println(result);
        
        // 通过Ognl可以创建一个初始化的List
        expression = Ognl.parseExpression("{'a', 'b', 'c', 'd'}");
        result = Ognl.getValue(expression, context, context.getRoot());
        System.out.println(result);
        
        // 通过Ognl可以创建一个初始化的Map,注意此时得加上#符号
        expression = Ognl.parseExpression("#{'a':'aa', 'b':'bb', 'c':'cc', 'd':'dd'}");
        // 创建指定类型的Map
        // expression = Ognl.parseExpression("#@java.util.TreeMap@{'a':'aa', 'b':'bb', 'c':'cc', 'd':'dd'}");
        result = Ognl.getValue(expression, context, context.getRoot());
        System.out.println(result);
        
        // 通过Ognl访问数组中的元素
        String[] name1 = {"liu", "xu"};
        context.put("name1", name1);
        // 直接通过数组名+下标
        expression = Ognl.parseExpression("#name1[1]");
        result = Ognl.getValue(expression, context, context.getRoot());
        System.out.println(result);
        
        // 通过Ognl访问集合中的元素
        List  name2 = new ArrayList();
        Collections.addAll(name2, name1);
        context.put("name2", name2);
        // 直接通过集合名+下标
        expression = Ognl.parseExpression("#name2[0]");
        result = Ognl.getValue(expression, context, context.getRoot());
        System.out.println(result);
        
        // 通过Ognl访问Map中的元素
        Map name3 = new HashMap();
        name3.put(1, "liu");
        name3.put(2, "xu");
        context.put("name3", name3);
        // 直接通过map名+key
        expression = Ognl.parseExpression("#name3[1]");
        result = Ognl.getValue(expression, context, context.getRoot());
        System.out.println(result);
    }
}

输出

[]
[a, b, c, d]
{a=aa, b=bb, c=cc, d=dd}
xu
liu
liu

注意点
1.创建集合不用加#,创建map要加#
2.创建类的一个对象,要使用类的完整路径
3.要创建带有初始化值的指定类型的List或Map,可以这样#@java.util.TreeMap@{'key':'value','key':'value',......}

6.Ognl中的投影和过滤

无论投影还是过滤,都是针对于数组、集合和Map而言。

投影:把集合中所有对象的某个属性抽出来,单独构成一个新的集合对象。语法如下

collection.{expression}

过滤:将满足条件的对象,构成一个新的集合返回。语法如下

collection.{?|^|$ expression}

上面?^$的含义如下
?:获得所有符合逻辑的元素。
^:获得符合逻辑的第一个元素。
$:获得符合逻辑的最后一个元素。

了解了上面的,我们就看看下面的代码

public class Test3 {
    public static void main(String[] args) throws OgnlException {
        Teacher t1 = new Teacher("liu", "男", 22);
        Teacher t2 = new Teacher("xu", "女", 22);
        Teacher t3 = new Teacher("qian", "男", 30);
        Teacher t4 = new Teacher("li", "女", 35);
        
        List lists = new ArrayList();
        Collections.addAll(lists, new Teacher[] {t1, t2, t3, t4});
        
        OgnlContext context = new OgnlContext();
        context.put("teachers", lists);
        // 把所有老师的名字拿出来,投影
        Object expression = Ognl.parseExpression("#teachers.{name}");
        Object result = Ognl.getValue(expression, context, context.getRoot());
        System.out.println(result);
        
        // 把年龄在20-30之间的所有老师拿出来,过滤
        expression = Ognl.parseExpression("#teachers.{? #this.age > 20 && #this.age < 30}");
        result = Ognl.getValue(expression, context, context.getRoot());
        System.out.println(result);
        
        // 把年龄在20-30之间的第一个老师拿出来,过滤
        expression = Ognl.parseExpression("#teachers.{^ #this.age > 20 && #this.age < 30}");
        result = Ognl.getValue(expression, context, context.getRoot());
        System.out.println(result);
        
        // 把年龄在20-30之间的最后一个老师拿出来,过滤
        expression = Ognl.parseExpression("#teachers.{$ #this.age > 20 && #this.age < 30}");
        result = Ognl.getValue(expression, context, context.getRoot());
        System.out.println(result);
    }
}

输出

[liu, xu, qian, li]
[Teacher [name=liu, gender=男, age=22], Teacher [name=xu, gender=女, age=22]]
[Teacher [name=liu, gender=男, age=22]]
[Teacher [name=xu, gender=女, age=22]]

这个没啥好讲的,记住语法就行了。

你可能感兴趣的:((九)Struts2进阶之OGNL表达式第一弹)