OGNL详解及其解析源码分析

OGNL详解及其解析源码分析

OGNL详解及其解析源码分析

标签:

Java, 协议与规范

保留所有版权,请引用而不是转载本文(原文地址

https://yeecode.top/blog/58/

)。

OGNL(Object Graph Navigation Language,中文名为:对象图导航语言)是一种功能强大的表达式语言(Expression Language,简称EL)。通过它,能够完成从集合中选取对象、读写对象的属性、调用对象和类的方法、表达式求值与判断等操作。

OGNL应用十分广泛,例如同样是获取Map中某个对象的属性,用Java语言表示出来如下:

userMap.get("user2").getName();

而使用OGNL表达式则为:

#user2.name

除了简单清晰以外,OGNL有着更高的环境适应性。我们可以将OGNL表达式应用在配置文件、XML文件等处,而只要在解析这些文件时使用OGNL进行解析即可。例如,下面所示的一段XML配置中,test条件的判断就使用了OGNL表达式。

那Java程序如何解析OGNL中的表达式呢?

OGNL有Java工具包,只要引入它我们便可以在Java中使用OGNL的功能。这样我们就可以使用Java来解析引入了OGNL的各种文档。在介绍OGNL用法之前,我们先介绍OGNL解析时要接触的三个重要概念:

表达式(expression):是一个带有语法含义的字符串,是整个OGNL的核心内容。通过表达式来确定需要进行的OGNL操作。

根对象(root):可以理解为OGNL的被操作对象。表达式中表示的操作就是针对这个对象展开的。

上下文(context):整个OGNL处理时的上下文环境,该环境是一个Map对象。在进行OGNL处理之前,我们可以传入一个初始化过的上下文环境。

例如在下面代码中,Java便使用OGNL完成了根对象信息的读写操作、上下文信息的读写操作。并且,整个操作过程中也支持逻辑表达式的判断等。

User user01 = new User(1, "易哥", 18);

User user02 = new User(2, "莉莉", 15);

Map userMap = new HashMap<>();

userMap.put("user1", user01);

userMap.put("user2", user02);

// 使用表达式读写根对象中信息的示例

// 该示例中要用到的OGNL函数:

// getValue(String expression, Object root) :对root内容执行expression中的操作,并返回结果

// 读取根对象的属性值

Integer age = (Integer) Ognl.getValue("age", userMap.get("user1"));

System.out.println("读取根对象属性,得到age:" + age);

// 设置根对象的属性值

Ognl.getValue("age = 19", userMap.get("user1"));

age = (Integer) Ognl.getValue("age", userMap.get("user1"));

System.out.println("设置根对象属性后,得到age:" + age);

// 使用表达式读写环境中信息的示例

// 该示例中要用到的OGNL函数:

// getValue(String expression, Map context, Object root) :在context环境中对root内容执行expression中的操作,并返回结果

// 读取环境中的信息

String userName2 = (String) Ognl.getValue("#user2.name", userMap, new Object());

System.out.println("读取环境中的信息,得到user2的name:" + userName2);

// 读取环境中的信息,并进行判断

Boolean result = (Boolean) Ognl.getValue("#user2.name != '丽丽'", userMap, new Object());

System.out.println("读取环境中的信息,并进行判断,得到:" + result);

// 设置环境中的信息

Ognl.getValue("#user2.name = '小华'", userMap, new Object());

String newUserName = (String) Ognl.getValue("#user2.name", userMap, new Object());

System.out.println("设置环境中的信息后,得到user2的name:" + newUserName);

上述的运行结果如下所示。

OGNL不仅可以读写信息,还能调用对象、类中的方法。例如运行下面代码:

// 调用对象方法

Integer hashCode = (Integer) Ognl.getValue("hashCode()", "yeecode");

System.out.println("对字符串对象调用hashCode方法得到:" + hashCode);

// 调用类方法

Double result = (Double)Ognl.getValue("@java.lang.Math@random()", null);

System.out.println("调用Math类中的静态方法random,得到:" + result);

便可以得到输出:

OGNL支持表达式的预编译,对表达式进行预编译后,避免了每次执行表达式前的编译工作,能够明显地提高OGNL的执行效率。

我们通过下面代码组建一个对比试验:

User user01 = new User(1, "易哥", 18);

User user02 = new User(2, "莉莉", 15);

Map userMap = new HashMap<>();

userMap.put("user1", user01);

userMap.put("user2", user02);

String userName;

// 先对表达式解析,然后再执行可以提高效率

long time1 = new Date().getTime();

// 解析表达式

Object expressionTree = Ognl.parseExpression("#user2.name");

// 重复运行多次

for (int i = 0; i < 10000; i++) {

userName = (String) Ognl.getValue(expressionTree, userMap, new Object());

}

long time2 = new Date().getTime();

// 直接重复运行多次

for (int i = 0; i < 10000; i++) {

userName = (String) Ognl.getValue("#user2.name", userMap, new Object());

}

long time3 = new Date().getTime();

System.out.println("编译之后再执行,共花费" + (time2 - time1) + "ms");

System.out.println("不编译直接执行,共花费" + (time3 - time2) + "ms");

结果如下:

可见,如果要多次运行一个表达式,则先将其编译后再运行的执行效率更高。

到此,我们了解了OGNL的威力。我们在JSP、XML中常常见到OGNL表达式,而这些表达式的解析就是通过本小节中介绍的方式进行的。可见,OGNL是一种广泛、便捷、强大的语言。

以上内容参考自《通用源码阅读指导书——MyBatis源码详解》一书。

《通用源码阅读指导书》

这是一本以MyBatis的源码为实例讲述源码阅读方法的书籍,并且附带有示例项目源码,MyBatis的全中文注解。书籍还总结了大量的编程知识和架构经验,对提升编程和架构能力十分有用。

书中还介绍了MyBatis如何完成XML中的OGNL解析,以及后续的变量替换等等。感兴趣的读者可以阅读本书。

可以访问个人知乎阅读更多文章:易哥(https://www.zhihu.com/people/yeecode),欢迎关注。

作者书籍推荐

易哥 | yeecode.top

相关推荐

【浅显易懂系列】目标跟踪各大派别的划分
365下载手机版

【浅显易懂系列】目标跟踪各大派别的划分

📅 07-17 👁️ 502
多线程高并发场景下为什么需要锁
365下载手机版

多线程高并发场景下为什么需要锁

📅 07-18 👁️ 1569
《winkawaks街机模拟器》v1.65最新版
365下载手机版

《winkawaks街机模拟器》v1.65最新版

📅 07-07 👁️ 2713