OGNL语法介绍

30 Mar 2023 » java

什么是OGNL?

来源官方

OGNL stands for Object-Graph Navigation Language; it is an expression language for getting and setting properties of Java objects, plus other extras such as list projection and selection and lambda expressions.

OGNL(Object-Graph Navigation Language)是对象导航图语言,是一种用于java对象get、set属性以及其他额外的投影、选择过滤、lambda表达式的的表达式语言。

OGNL读为orthogonal[ɔ:’θɒgənl]。

Java中有哪些常见的框架/项目使用了OGNL?

常量

字符串

单引号或双引号包围,如"Hello"'World'

字符

单引号包围,如'a''1'

数字

在数字后添加特定后缀表示不同的类型

后缀类型
java.lang.Integer
l/Ljava.lang.Long
f/Fjava.lang.Float
d/Djava.lang.Double
b/Bjava.math.BigDecimal
h/Hjava.math.BigInteger

布尔值

truefalse

空值

null

属性访问及集合索引

数组

定义数组#array=new int[]{1, 2, 3}

  • 获得数组大小

    #array.length#array["length"]#array["len" + "gth"]

  • 获得索引元素

    #array[0]

List

定义List#list={1, 2, 3}

  • 获得List大小

    #list.size#list["size"]

  • 获得索引元素

    #list[0]#list.get(0)

  • 访问无参方法

    #list.toArray#list.iterator

Map

定义Map#map=#{"foo": "foo value", "bar": "bar value", "values": "values value"}

  • 获得Map大小

    #map.size

  • 获得key的值 #map.foo#map["foo"]#map.get("foo")

    注意

    当存在key与map的方法冲突时,使用#map.values实际返回结果是map的值集合而非"values value", 所以精确访问key的值建议使用后两种方式

  • 访问无参方法

    #map.values#map.keys.iterator#map.isEmpty

普通Java对象

定义普通对象如下:

package demo;

public class Person {
    String name;
    Integer age;
    Sex sex;

    // 省略getter/setter
}

enum Sex {
    MALE,
    FEMALE,
    UNKNOWN
}

ognl对象声明脚本#person = new Person(), #person.name="张三", #person.age=11, #person.sex = @demo.Sex@valueOf("MALE")

  • 属性访问

    #person.name#person.age#person.sex

  • 方法访问

    #person.toString()

静态属性访问

需要全名称访问@fullClassName@fieldName,如@java.util.Random@mask、获得某个类的class@java.lang.String@class

变量声明定义

#后面跟变量定义

注意

#也用于map定义,#map=#{"foo": "foo value", "bar": "bar value"}

  • #integer=1
  • #big=1H

子表达式

在表达式.后面添加括号(),括号内则为子表达式,子表达式独立运行且能获得点之前的表达式结果。

#num=1, (#num + 5).(#this * #this, #this - 5)

lambda表达式

通过:[expression]定义。

定义一个阶乘lambda表达式,#fact=:[#this <= 1 ? 1 : #this*#fact(#this-1)], #fact(30H)

集合构造

数组

类似java的数组声明,#array=new int[]{1, 2, 3}#array=new String[]{"a", "b", "c"}

List

#list={1, 2, 3, 4},判断是否在list中,1 in #list,默认类型为java.util.ArrayList

Map

#map=#{"key": "value", "key1": "value1"},默认类型为java.util.LinkedHashMap, 若要修改为其他类型的Map,#map=#@java.util.HashMap@{ "foo" : "foo value", "bar" : "bar value" }

集合的投影、过滤、选择

类似Java的Stream的map、filter操作。

#list={1, 2, 3, 4, 5}

投影

将列表中的数据做平方后返回投影list,#list.{ #this * #this }

过滤

选择列表中的奇数返回list,#list.{? #this % 2 == 1 }

选择

  • 选择第一个匹配的元素

    找到列表中第一个偶数,#list.{^ #this%2 == 0 },返回结果还是list而非object

  • 选择最后一个匹配的元素

    制造到列表中最后一个偶数,#list.{$ #this%2 == 0},返回结果还是list而非object

类型强转

OGNL可以强制object转为布尔、数字、整数、集合。

强转为布尔值

所有需要布尔值的地方(如三元表达式),object都会相应的转为布尔值

  • 对象本身就是布尔类型(boolean)

    则为其本身

  • 对象是数字类型(Number)

    非0为true,0为false

  • 对象是字符类型(Character)

    非0为true,0为false

  • 其他类型

    非空为true,则为false

数字类型转换

数字操作(+、-、*、/)设计到两个参数,将会按照以下方式决定其结果。

  • 两个参数类型一样

    结果数字类型则一致,如1+1结果为int、1f+1f结果为float、1b+1b结果为BigDecimal

  • 有一个参数是非数字类型

    结果将数字当作double类型处理

  • 两个参数都是浮点型

    结果将是较大的浮点类型,如1+1f结果为float、1f+1d结果为double、1+1b结构为BigDecimal

  • 两个参数都是整型

    结果将是较大的整型类型,如'1' + 1结果为integer、1+true结果为integer、1L+1结果为long、1+1h结果为BigInteger

  • 一个为浮点型、另一个为整型

    如果整数比int小结果为double,如果整数比int大结果为BigDecimal

强转为整数

位操作只针对整型,如果是BigDecimal或BigInteger结果为BigInteger, 否则保留操作类型相同类型且只取整数部分。

强转为集合

投影(e1.{e2})、选择(e1.{? e2})及in(e1 in e2)操作,将强转其中一个参数为集合然后遍历。

  • 数组类型

    直接从头到尾遍历,1 in new int[]{1, 2, 3}new int[]{1, 2, 3}.{ #this + 1 }

  • java.util.Collection子类

    按照迭代器遍历,1 in {1, 2, 3}{1, 2, 3}.{ #this + 1 }

  • java.util.Map子类

    按照map的值的迭代器遍历,#{"a":1,"b": 2,"c": 3}.{ #this }结果为{1, 2, 3}

  • java.lang.Number

    从0开始遍历,#num=2, #num.{ #this + 1}结果为{1, 2}#num=2.5d, #num.{ #this + 1}结果为{1.0d, 2.0d}

  • 其他类型

    将其当做只有一个元素的集合

附录(OGNL中的操作符)

操作符描述结果值描述
e1 , e2序列e1、e2表达式都会执行,结果为e2
e1 = e2赋值执行e2,赋值e1,结果为e2
e1 ? e2 : e3三元条件操作符执行e1,根据结果为true/false决定执行并返回结果e2/e3
e1 || e2
e1 or e2
逻辑或如果e1为true则只执行e1,否则再执行e2,结果为其中真值
e1 && e2
e1 and e2
逻辑与如果e1为false则只执行e1并返回结果e1,否则再执行e2并返回e2
! e
not e
逻辑非将e解释为布尔值后取反则为结果
e1 | e2
e1 bor e2
位运算或将e1和e2解释为整型并进行或运算结果
e1 ^ e2
e1 xor e2
位运算异或将e1和e2解释为整型并进行异或运算结果
e1 & e2
e1 band e2
位运算与将e1和e2解释为整型并进行与运算结果
~ e位运算非将e解释为整型并进行非运算结果
e1 << e2
e1 shl e2
位运算左移将e1、e2解释为整型并左移运算结果
e1 >> e2
e1 shr e2
位运算右移将e1、e2解释为整型并右移运算结果
e1 >>> e2
e1 ushr e2
位运算无符号右移将e1、e2解释为整型并无符号右移运算结果
e1 + e2和运算将e1、e2解释为整型并求和运算结果,若是字符串则结果为字符串拼接结果
e1 - e2差运算将e1、e2解释为整型并求差运算结果
e1 * e2乘运算将e1、e2解释为整型并求乘积运算结果
e1 / e2除运算将e1、e2解释为整型并求商运算结果
e1 % e1余运算将e1、e2解释为整型并求余运算结果
+ e正数符号表示正数
- e负数符号表示负数
e1 < e2
e1 lt e2
小于将e1、e2通过compareTo方法比较,结果为布尔值
e1 <= e2
e1 lte e2
小于等于将e1、e2通过compareTo方法比较,结果为布尔值
e1 > e2
e1 gt e2
大于将e1、e2通过compareTo方法比较,结果为布尔值
e1 >= e2
e1 gte e2
大于等于将e1、e2通过compareTo方法比较,结果为布尔值
e1 in e2包含集合e2是否包含e1元素,结果为布尔值
e1 not in e2不包含集合e2是否不包含e1元素,结果为布尔值
e1 == e2
e1 eq e2
equals同java中Object的equals方法,结果为布尔值
e1 != e2
e1 neq e2
not equals同java中Object的equals方法取反,结果为布尔值
e instanceof class判断实例是否是给定class类型结果为布尔值,class必须是全名称
e.method(args)方法调用结果为方法返回值
e.property属性值获取结果为属性值
@class_name@method(args)静态方法调用类名为全名,结果为方法调用结果
@class_name@field静态字段访问类名为全名,结果为字段值
e1[e2]索引获得集合、数组索引位置的值
e1.{ e2 }投影集合投影
e1.{? e2 }选择集合选择过滤
e1.{^ e2 }选择第一个集合过滤选择满足条件的第一个元素
e1.{$ e2 }选择最后一个集合过滤选择满足条件的最后一个元素
e1.( e2, e3, e4 )子表达式执行e1后执行e2、e3、e4子表达式,结果为e4
e1( e2, e3, e4 )表达式执行执行e1后执行e2、e3、e4子表达式,结果为e1
#variable变量引用/声明变量引用/声明
new type[]{ e, … }数组创建创建一个数组, type为任意类型
{ e, … }列表创建创建一个列表,结果为ArrayList
#{ k1: v1, k2: v2, … }Map创建创建一个Map,结果为LinkedHashMap
#@class_name@{ k1: v1, k2: v2, … }指定Map类型创建类名为全名,如#@java.util.HashMap@{k1: v1, k2: v2}
:[ e ]lambda表达式定义如上的阶乘定义