与MVC框架解耦的OGNL:前世今生及其基本用法

Struts2框架核心概念与实战工具详解 Struts2是Apache Software Foundation管理的开源Web应用框架,起源于经典的Struts框架,并在2007年引入了WebWork框架的优秀特性,形成了全新的Struts2框架。最初由Craig McClanahan创建,旨在简化Web层开发的复杂性,并提供了一套丰富的工具和组件以支持模型-视图-控制器(MVC)设计模式。Struts2自推出以来,因其易于学习和使用,迅速成为Java社区中广受欢迎的框架之一。 阅读详情

摘要:

  虽然我们一般都是通过学习MVC框架而结缘OGNL,但它并没有与MVC框架紧紧耦合在一起,而是一个以独立的库文件出现的一种功能强大的表达式语言,也是字符串与Java对象之间沟通的桥梁。特别地,正因为它的独立性,使得我们可以十分方便地利用它构建我们自己的框架。在充分理解和掌握 OGNL 三要素 后,我们就可以通过简单一致的语法去存取Java对象树中的任意属性、调用Java对象树的方法并自动实现必要的类型转化。本文首先概述了Ognl的前世今生,并结合具体实例和源码阐述了OGNL的实质和OGNL三要素,介绍了对Java对象属性值的访问,静态、实例和构造方法的调用,对容器的访问以及对集合的操作等五个方面的介绍,奠定了学习OGNL的基础。


版权声明:

本文原创作者:书呆子Rico 
作者博客地址:http://blog.youkuaiyun.com/justloveyou_/


友情提示:

  为了更好地了解 OGNL,笔者将以两篇博客的篇幅来介绍OGNL:《与MVC框架解耦的OGNL:前世今生及其基本用法》 和 《再述OGNL:在Struts2中的应用》。其中,在本文(《与MVC框架解耦的OGNL:前世今生及其基本用法》)中,我们首先介绍了OGNL的前世今生,并结合具体实例和源码阐述了OGNL的实质、OGNL三要素和用法语法。在此基础上,本篇的姊妹篇《再述OGNL:在Struts2中的应用》详尽地介绍了OGNL的在Struts2中的应用。


一. OGNL 概述

1、OGNL的前世今生

  WebWork是建立在称为XWork的Command模式框架之上的强大的基于Web的MVC框架。关于WebWork我们大多数人可能不太熟悉,最多只是有一种在哪里见过的感觉,但是我一提Struts2,估计大家就能想起来了。众所周知,Struts2是Struts的下一代产品,是在Struts1和WebWork的技术基础上进行了合并的全新框架。需要特别注意的是,全新的Struts2的体系结构与Struts1差别巨大,因为Struts2是以WebWork为核心的,继承了更多的WebWork血统。

  实际上,WebWork 已经完全从Web层脱离出来的一个非常优秀的框架,其提供了很多核心的、Struts2还在使用的功能,包括前端拦截器(interceptor)、运行时表单属性验证、类型转换、IoC(Inversion of Control)容器等,其中就有我们今天的主角,强大的表达式语言 —— OGNL(Object Graph Notation Language)。


2、OGNL 带给我们的实惠

  OGNL 是 Object Graph Navigation Language 的缩写,全称为 对象图导航语言,是一种功能强大的 表达式语言。它通过简单一致的语法,可以存取Java对象树中的任意属性、调用Java对象树的方法,并能够自动实现必要的类型转化。更形象地说,如果我们把OGNL表达式看做是一个带有语义的字符串,那么OGNL无疑是这个语义字符串与Java对象之间沟通的桥梁。

  我们知道,在我们使用 MVC 架构模式进行开发Web应用时,数据往往需要在各层之间进行流转。由于数据在不同层次中的表现形式不尽相同,所以这种流转会很麻烦,特别是在Controller与View之间进行流转。实际上,数据在Controller层与View层之间流转的真正痛点就在于:数据在View层(视图页面)的表现形式是一个扁平的、不带任何数据类型的字符串,而在Controller层(Java世界)完全可以表现为一个具有丰富数据结构和数据类型的Java对象,正是由于这种数据表现形式的差异,导致我们手工执行这种转换将是一项非常复杂、低效的工作。正因为如此,为了更好地解决数据在不同层之间的数据流转问题,作为一个优秀成熟的框架,Struts2 集成了 WebWork 中的 OGNL 来帮助我们解决个问题。因此,当我们在使用Struts2时,会发现OGNL充斥在前后台数据传递与存储的方方面面,也给我们带来了极大的方便。


  Ps:更多关于 MVC框架数据流转问题与OGNL在Web中的魅力 等内容请读者移步本文的姊妹篇《再述OGNL:在Struts2中的应用》。


3、小结

  OGNL是模板语言的一个重要补充,对表现层技术而言是一次里程碑式的进步。在我们常见的视图组件,包括 Jsp 2.0,Velocity,Jelly 等,虽然都有类似的功能,比如,在 Jsp 2.0 中我们可以使用其提供的 EL 表达式完成类似的功能。但是,OGNL比它们要完善的多得多,而且以一个独立的库文件出现,十分方便我们构建自己的框架。


二. OGNL 深度解读:从一个例子说起

  我们在上文已经提到,OGNL 以一个独立的库文件出现,十分方便我们构建自己的框架。那么,我们首先新建一个Java Project,然后从Struts2的相关依赖包中导入 ognl-x.x.xx.jar (本人使用的struts-2.1.6中的ognl-2.6.11.jar),搭建完毕后项目结构如下:

                Image 4.png-8.8kB

  Ps:若读者不知如何重现笔者获取OGNL的jar包的过程,请笔者移步我的博文《Struts2 实战:从 登录Demo 看 Struts2 应用开发》去详细了解其获取过程,此不赘述。另外,由于OGNL是Apache开源项目的子项目,所以我们可以从 Apache Commons 下载OGNL的jar包。


1、OGNL应用实例

  上述的Java Project包含两个JavaBean类和一个OGNL测试类,我们将围绕这个Project展开对OGNL的介绍。我们先看一下该Project中各个类的源码:


(1). 两个JavaBean

package cn.tju.edu.rico.test;

import java.util.HashSet;
import java.util.Set;

// Java Bean : Student
public class Student {

    private College College;  

    private String name;  
    private String gentle;  
    private double height;
    private int age; 

    // 无参构造器
    public Student() {  
    }  

    public Student(String name, int age, double height) {
        super();
        this.name = name;
        this.height = height;
        this.age = age;
    }

    //getter & setter
    public College getCollege() {  
        return College;  
    }  

    public void setCollege(College College) {  
        this.College = College;  
    }  

    public String getName() {  
        return name;  
    }  

    public void setName(String name) {  
        this.name = name;  
    }

    public String getGentle() {
        return gentle;
    }

    public void setGentle(String gentle) {
        this.gentle = gentle;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student [name=" + name + ", height=" + height + ", age=" + age
                + "]";
    } 
}



// Java Bean : College
class College {

    private String name;  

    private Set<Student> Students = new HashSet<Student>();  

    // 无参构造器
    public College() {  
    }  

    //getter & setter
    public String getName() {  
        return name;  
    }  

    public void setName(String name) {  
        this.name = name;  
    }  

    public Set<Student> getStudents() {  
        return Students;  
    }  

    public void setStudents(Set<Student> Students) {  
        this.Students = Students;  
    }  
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104

(2). OGNL测试类

package cn.tju.edu.rico.test;

import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;

public class OGNLTest {

    public static void main(String[] args) throws OgnlException {

        // 新建一个学校对象
        College college = new College();
        college.setName("TJU");

        // 新建一个学生对象
        Student stu = new Student();
        stu.setName("Rico");

        // 构建一个OgnlContext对象 ,并将上述学校、学生对象放入Ognl上下文环境(本质是一个Map)中
        OgnlContext context = new OgnlContext();
        context.put("college", college);
        context.put("stu", stu);

        // 将学生设置为根对象
        context.setRoot(stu);

        // 构建Ognl表达式的树状表示
        Object expression1 = Ognl.parseExpression("#college.name");
        Object expression2 = Ognl.parseExpression("name");
        Object expression3 = Ognl.parseExpression("#stu.name");

        // 根据Ognl表达式给Java对象设置值,将TJU改为NEU
        Ognl.setValue(expression1, context, context.getRoot(), "NEU");

        // 根据Ognl表达式获取Java对象的(属性)值
        Object collegeName = Ognl.getValue(expression1, context,
                context.getRoot());
        Object stuName2 = Ognl
                .getValue(expression2, context, context.getRoot());
        Object stuName3 = Ognl
                .getValue(expression3, context, context.getRoot());

        System.out.println(collegeName);
        System.out.println(stuName2);
        System.out.println(stuName3);
    }
}/* Output: 
        NEU
        Rico
        Rico
 *///:~
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

  上面的输出结果对我们来说一点也不意外,因为在Struts2中,我们常常使用上述方式访问Stack Context(Action Context)及其根对象Value Stack。根据这个例子我们也能够看出,所谓的对象图导航语言本质上就是通过 类似”放置到OgnlContext中的名字.属性名字” 的方式去获取对应对象的属性值。特别的是,对于根对象的属性的访问,我们只需直接利用属性名字访问即可,因为根对象只有一个,OGNL会默认从OgnlContext中的根对象中去寻找;而对于普通对象的属性的访问,我们使用类似”#放置到OgnlContext中的名字.属性名字”的方式去访问,这时OGNL在解析表达式的时候发现表达式开头带有”#”,就会去普通对象中去寻找。当然,使用这种方式也可以访问根对象的属性,但是若在访问普通对象时不加前缀“#”,将会抛出 ognl.OgnlException。


2、OGNL 三要素

  事实上,OGNL表达式的计算是围绕OGNL上下文(OgnlContext)进行的,而OGNL上下文实际上就是一个Map对象。我们从上述的例子可以看出,无论是setValue方法还是getValue方法,它们均包含三个核心参数,即 tree(OGNL表达式), context(Ognl上下文), root(Ognl上下文的根对象),我们将其称之为 OGNL的三要素,因为 OGNL 的操作实际上就是围绕着这三个参数而进行的。这三者的关系如下图所示:

            这里写图片描述


(1).表达式(Expression)

  表达式是整个OGNL的核心,所有的OGNL操作都是对表达式解析后进行的。准确的来说,表达式表达了此OGNL操作的语义,即表明了此OGNL操作“要干什么”。


(2).上下文环境(Context)

  我们在上文提到,所有的OGNL操作都是在一个特定的环境中进行的,这个环境就是OGNL的上下文环境(OGNL Context)。更直白地说,OGNL上下文为OGNL表达式的提供了具体的运行环境。需要指出的是,我们完全可以像操作Map那样将一些数据设置到OGNL Context中,以便我们通过OGNL访问。准确的来说,Context为OGNL表达式提供了具体环境,为OGNL操作“提供支持”。


(3).根对象(Root Object)

  根对象是OGNL Context中的一员,并且整个OGNL Context最多只允许有一个根对象。也就是说,OGNL Context中共有两类对象,即 根对象 和 普通对象 ,它们的差异具体表现在访问方式上,我们针对根对象的存取操作的表达式不需要增加任何前缀(下文会具体提到)。根对象从侧面指明了OGNL操作所针对的对象类别,也就是说,在表达式规定了“干什么”之后,根对象指明了我们到底“对谁干”(根对象还是普通对象)。


3、OGNL源码解读

  在上述的例子中,无论是setValue方法还是getValue方法,都是ognl.Ognl类提供的两个静态方法。事实上,在OGNL中,我们最常用到的两个类是 ognl.Ognl 与 ognl.OgnlContext 。ognl.Ognl类是一个抽象类,并提供了一系列用于解析和解释执行Ognl表达式的方法,而抽象类则是专门用来继承的;ognl.OgnlContext类则为Ognl表达式提供了一个执行环境,这个类实现了Map接口,所以允许我们通过使用Map的put(key,value)方法向OgnlContext添加各种类型的对象。需要注意的是,在OgnlContext中一共有两种对象,第一种是根对象,根对象在整个OgnlContext中有且最多只能有一个,我们可以通过调用OgnlContext.setRoot(obj)设置根对象。另外一种就是普通对象,它的个数不受限制。它们最重要的一个区别是在对象属性的获取方式上,前者可直接访问,后者需使用类似”#放置到OgnlContext中的名字.属性名字”的方式去访问 。下面给出了ognl.Ognl 与 ognl.OgnlContext 的声明方式,关于它们更多的细节本文不在赘述,读者若想进一步了解,请自行阅读源码。

// Ognl 是一个抽象类,而抽象类则是专门用来继承的
public abstract class Ognl {
    ...
}
 
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
// OgnlContext 是一个Map
public class OgnlContext extends Object implements Map {
    ...
}
 
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

  Ps:关于 抽象类的深层次表述 请读者移步我的博文《Java 的抽象特性:抽象类与接口深度解析》。


4、小结

  到此为止,我相信通过上面的例子和表述,我们对Ognl表达式有了一个更深入的了解和认识。此外,我们知道对于普通对象的属性的访问,我们只能使用类似”#放置到OgnlContext中的名字.属性名字”的方式去访问,而对于根对象的属性的访问,我们可以通过以下两种方式去访问:

  • 直接利用属性名字访问;

  • 类似”#放置到OgnlContext中的名字.属性名字”的方式去访问; 
      
       
    下文我们将着重讲述Ognl的基本用法,抛开MVC框架单独了解它的用法便于我们进一步理解Ognl在Struts2中的使用方式。


三. 使用 OGNL 去访问方法

  我们除了利用Ognl表达式访问对象的属性,还可以使用它来访问方法。当然,对于方法的访问,又可以分为 对静态方法的访问、对实例方法的访问 和 对构造方法的访问,我们先看下面的例子:

package cn.tju.edu.rico.test;

import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;

public class OGNLTest2 {
    public static void main(String[] args) throws OgnlException {

        // 新建一个学校对象
        College college = new College();
        college.setName("NEU");

        // 新建一个学生对象
        Student stu = new Student();
        stu.setName("Livia");
        stu.setCollege(college);
        stu.setGentle("boy");

        // 构建一个OgnlContext对象,并将上述学校和学生对象放入Ognl上下文环境中
        OgnlContext context = new OgnlContext();
        context.put("college", college);
        context.put("stu", stu);

        // 将学生对象设置为根对象
        context.setRoot(stu);

        // 访问实例方法
        Object expression1 = Ognl.parseExpression("getGentle()");
        Object length1 = Ognl.getValue(expression1, context, context.getRoot());
        Object expression2 = Ognl.parseExpression("#college.name.length()");
        Object length2 = Ognl.getValue(expression2, context, context.getRoot());
        System.out.println(length1);
        System.out.println(length2);

        // 访问静态方法
        Object expression3 = Ognl.parseExpression("@java.lang.Math@max(2,4)");
        Object length3 = Ognl.getValue(expression3, context, context.getRoot());
        Object expression4 = Ognl
                .parseExpression("@java.lang.String@valueOf(name.length())");
        Object length4 = Ognl.getValue(expression4, context, context.getRoot());
        System.out.println(length3);
        System.out.println(length4);

        // 访问构造方法:通过Ognl表达式构建一个LinkedList对象,注意使用全类名
        Object expression5 = Ognl.parseExpression("new java.util.LinkedList()");
        List list = (List)Ognl.getValue(expression5, context, context.getRoot());
        list.add("list");
        list.add("rico"); 
        System.out.println(list);
    }
}/* Output: 
        boy
        3
        4
        5
        [list, rico]
 *///:~
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

四. 使用 OGNL 去访问容器对象

   我们还可以利用Ognl表达式访问容器对象,包括数组,List,Set,Map等,看下面的例子:

package cn.tju.edu.rico.test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;

public class OGNLTest3 {

    public static void main(String[] args) throws OgnlException {

        OgnlContext context = new OgnlContext();

        // 处理数组类型
        String[] strs = new String[] { "a", "b", "c" };
        context.put("strs", strs);
        System.out.println(Ognl.getValue("#strs[2]", context, context.getRoot()));

        // 处理List类型
        List<String> words = new ArrayList<String>();
        words.add("rico");
        words.add("livia");
        words.add("neu");
        context.put("words", words);
        System.out.println(Ognl.getValue("#words[0]", context,context.getRoot()));


        // 处理Map类型
        Map<String,String> map = new HashMap<String,String>();
        map.put("ad", "d");
        map.put("Rico", "China");
        map.put("campus", "neu");
        context.put("map", map);
        System.out.println(Ognl.getValue("#map['Rico']",context, context.getRoot()));



        // 处理Set类型:由于Set的无序性,所以不能通过这种访问Set(只能迭代输出),会抛出 ognl.NoSuchPropertyException;
        Set<String> set = new HashSet<String>();
        set.add("rico");
        set.add("livia");
        set.add("neu");
        context.put("set", set);
        System.out.println(Ognl.getValue("#set[2]", context, context.getRoot()));
    }
}/* Output: 
        c
        rico
        China
        Exception in thread "main" ognl.NoSuchPropertyException: java.util.HashSet.2
 *///:~
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

  由于Set是无序的且没有索引,所以我们只能对其进行迭代输出。Struts2 提供了一组逻辑控制标签,其中就有iterator,它可以完美完成这件事情。关于Struts2 的逻辑控制标签的叙述详见本文的姊妹篇《再述OGNL:在Struts2中的应用》。

  由于HashSet就是通过HashMap实现的,所以更多关于 HashSet的了解,读者可以参考我的博文《Map 综述(一):彻头彻尾理解 HashMap》。


五. 使用 OGNL 对容器进行操作

  我们还可以利用Ognl表达式对容器对象作一些操作,比如 过滤 和 投影。过滤指的是将原集合中不符合条件的对象过滤掉,然后将满足条件的对象,构建一个新的集合对象返回,Ognl过滤表达式的写法是:collection.{?|^|$ expression};投影指的是将原集合中所有对象的某个属性抽取出来,单独构成一个新的集合对象返回,基础语法为 :collection.{expression}。特别需要注意的是,无论是过滤操作还是投影操作,它们的操作对象和操作结果都是一个容器。

package cn.tju.edu.rico.test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;

public class OGNLTest4 {
    public static void main(String[] args) throws OgnlException {  

        Student s1 = new Student("Tom", 22, 170.3);  
        Student s2 = new Student("Jack", 21, 176.2);  
        Student s3 = new Student("Tomas", 23, 180.1);  
        Student s4 = new Student("Lucy", 20, 163.3);  

        List<Student> stus = new ArrayList<Student>();  
        Collections.addAll(stus, s1, s2, s3, s4);  

        // 新建OgnlContext对象  
        OgnlContext context = new OgnlContext();  
        context.put("stus", stus);  

        // 过滤(filtering),collection.{? expression}  
        // 利用过滤获取身高在175以上的所有学生集合  
        // 输出结果:[Student [name=Jack, age=21, height=176.2], Student [name=Tomas, age=23, height=180.1]]  
        System.out.println(Ognl.getValue("#stus.{? #this.height > 175.0}", context, context.getRoot()));  

        // 过滤(filtering),collection.{^ expression}  
        // 利用过滤获取身高在175以上的所有学生集合中第一个元素  
        // 输出结果:[Student [name=Jack, age=21, height=176.2]]  
        System.out.println(Ognl.getValue("#stus.{^ #this.height > 175.0}", context, context.getRoot()));  

        // 过滤(filtering),collection.{$ expression}  
        // 利用过滤获取身高在175以上的所有学生集合的最后一个元素  
        // 输出结果:[Student [name=Tomas, age=23, height=180.1]]  
        System.out.println(Ognl.getValue("#stus.{$ #this.height > 175.0}", context, context.getRoot()));  

        // 投影(projection), collection. {expression}  
        // 获取集合中的所有学生的姓名  
        // 输出结果:[Tom, Jack, Tomas, Lucy]  
        System.out.println(Ognl.getValue("#stus.{name}", context, context.getRoot()));  
    }  
}/* Output: 
        [Student [name=Jack, height=176.2, age=21], Student [name=Tomas, height=180.1, age=23]]
        [Student [name=Jack, height=176.2, age=21]]
        [Student [name=Tomas, height=180.1, age=23]]
        [Tom, Jack, Tomas, Lucy]
 *///:~
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

六. 总结

  虽然我们一般都是通过学习MVC框架而结缘OGNL,但它并没有与MVC框架紧紧耦合在一起,而是一个以独立的库文件出现的一种功能强大的表达式语言,也是字符串与Java对象之间沟通的桥梁。特别地,正因为它的独立性,使得我们可以十分方便地利用它构建我们自己的框架。在充分理解和掌握 OGNL 三要素 后,我们就可以通过简单一致的语法去存取Java对象树中的任意属性、调用Java对象树的方法并自动实现必要的类型转化。本文首先概述了Ognl的前世今生,并结合具体实例和源码阐述了OGNL的实质和OGNL三要素,介绍了对Java对象属性值的访问,静态、实例和构造方法的调用,对容器的访问以及对集合的操作等五个方面的介绍,奠定了学习OGNL的基础。


七. 更多

  更多关于 MVC框架数据流转问题与OGNL在Web中的魅力 等内容请读者移步本文的姊妹篇《再述OGNL:在Struts2中的应用》。

  更多关于 抽象类的深层次表述 请读者移步我的博文《Java 的抽象特性:抽象类与接口深度解析》。

  由于HashSet就是通过HashMap实现的,所以更多关于 HashSet的了解,读者可以参考我的博文《Map 综述(一):彻头彻尾理解 HashMap》。

Struts 2.0.11全量包项目实战与框架解析 Struts 2 并非 Struts 1 的简单升级,而是基于 WebWork 框架演化而来的全新 MVC 实现,其核心设计理念在于解耦请求处理与 Servlet API。在 Struts 2.0.11 版本中,作为前端控制器,负责拦截所有匹配的 HTTP 请求,并启动框架的请求处理流程。请求进入后,会加载struts.xml配置文件,构建运行时配置模型;随后创建,通过代理模式封装 Action 实例的执行逻辑。// web.xml 中配置核心过滤器<filter> 阅读详情

相关推荐

Struts2框架详解与实战——基于张冰PPT的深度解析

创建一个用于判断用户是否登录的拦截器:@OverrideSystem.out.println("AuthInterceptor 初始化");@OverrideSystem.out.println("AuthInterceptor 销毁");@Overridectx.put("errorMessage", "您尚未登录,请先登录!");// 返回全局结果名// 继续执行后续流程逻辑分析:init()和destroy()分别在容器启动和关闭时调用,可用于资源初始化。

weixin_34163588的博客 648

接口的学习

接口的学习和理解

qq_48578877的博客 281

Struts2框架核心概念与实践

Struts2是一个开源的MVC(Model-View-Controller)框架,最初由Craig McClanahan发起,作为Apache软件基金会下的一个项目。Struts2是Struts框架的后续版本,借鉴了WebWork的设计,并提供了与JSF等技术集成的能力。随着版本的演进,Struts2不断完善,为开发者提供了稳定、高效、易用的Web应用开发解决方案。MVC(Model-View-Controller,模型-视图-控制器)设计模式是一种被广泛应用于用户界面交互开发的架构模式。

weixin_29476595的博客 1166

SSH与SSM学习之Struts208——OGNL表达式与Struts框架结合之原理

SSH与SSM学习之Struts208OGNL表达式与Struts框架结合之原理 一结合图示 二结合说明 三查看值栈中的内容 1 StackDemoAction 2 strutsxml 配置文件 3 messagejsp 4 Root部分 4 Context部分SSH与SSM学习之Struts208——OGNL表达式与Struts框架结合之原理一、结合图示二、结合说明OGNL表达式想要运行,需要一个

qiwenmingshiwo的专栏 435

Struts2.3.16:企业级Java Web应用框架精讲

Struts 2是一个用于构建Java Web应用的开源框架,它继承了Struts 1和WebWork 2的优势。自2007年发布以来,Struts 2成为Java EE开发中MVC(Model-View-Controller)模式的首选框架之一。它以Action类为核心,将业务逻辑与用户界面分离,提高了代码的可维护性与可扩展性。自Struts 2.0版本起,框架一直不断发展和改进,经历了数个重要的版本迭代。

weixin_35995661的博客 824

基于SSH框架的教务管理系统设计与实现

为了实现细粒度的访问控制,可定义自定义权限注解并通过AOP拦截:// 允许的角色列表切面实现:@Aspect@Component@Autowired// 封装SecurityUtils.getSubject()if (!if (!在服务层使用:@Service// 删除逻辑Shiro 的核心组件是Realm,它负责与数据源交互完成身份验证与授权。默认不支持数据库查询,需继承并重写两个关键方法。

weixin_42234168的博客 969

Struts标签库结合OGNL的使用

1.OGNL:object graph navigation language(对象图导航语言),一般是和Struts标签库结合使用。 2.值栈:ValueStack,是对应每一个请求对象的轻量级的内存数据中心。 3.

HLoach的博客 1070

OGNL(jar包)源代码(OGNL表达式实现代码)

OGNL通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。 现在提供源代码,供大家分享

什么是OGNL表达式

前言       在经典的SSH框架中,我们在JSP页面经常会用到OGNL表达式来取值,然后赋值给页面元素,这种方式来赋值,JSP页面的代码看起来干净整洁,没有过多的Java代码。但是到底什么是OGNL表达式呢?       说道OGNL表达式,我们可能会很快想到EL表达式。没错,他们都是很强大的表达式语言,但是EL表达式在Struts1中是比较常用的,Struts2则默认使用了OGNL表...

Coder Yasmine 1万+

基于Meteoinfo与TrajStat的后向轨迹聚类分析实战:从数据准备到可视化制图

本文详细介绍了基于Meteoinfo与TrajStat进行后向轨迹聚类分析的完整实战流程。从Java环境配置、Meteoinfo软件与TrajStat插件安装,到精准获取NOAA气象数据、设置参数进行批量轨迹计算,再到利用聚类分析提炼主要气流路径,并最终完成专业地图可视化与制图。文章旨在帮助环境与气象研究者快速掌握从数据准备到成果输出的全链路操作。

p4q5r6s7t的博客 805

【Tessent Shell Users Manual】【Ch3】Design Introspection and Editing(未完待续)

Tessent Shell 提供了一系列用于检查(examining/introspecting)和编辑design的命令。

Tranquil_ovo的博客 1731

再述 OGNL:在Struts2中的应用

MVC框架的分层结构提高了程序的可维护性、可移植性、可扩展性与可重用性,降低了程序的开发难度,但是也引来了一些新的问题,比如不同层次间的数据流转问题。OGNL的出现填平了这条沟壑,成为字符串与Java对象之间沟通的桥梁。本文首先概述了Struts2为何要集成WebWork的OGNL,并给出了Struts2官方对OGNL的描述,并就OGNL在Struts2中的进一步应用进行了详述,包括数据访问、控制标签等。

Rico's Blogs 3837

嵌入式:简单的service框架

在嵌入式中,我们获取一个资源、设置一个变量、更新数据的话最直接的方式就是直接调用一个函数。这样的写法也可以满足基本的功能,前提是这个系统是只要一个任务,只有一个while(1)如图1.1。图1.1当有两个或者两个以上的任务的时候,同时调用一个函数(设置一个状态值、操作同一个资源、操作指针等行为)的流程如图1.2。图1.2当两个任务同时操作一个资源的时候,就会有重入的问题,会导致想不到的异常问题(数据不对、指针异常等)。

stupid的博客 986

MVC框架简介

在Web应用程序设计中,MVC模式已经被广泛使用。SpringMVC以DispatcherServlet为核心,负责协调和组织不同组件以完成请求处理并返回响应的工作,实现了MVC模式。 1、mvc( mode view controller)是什么? m指业务代码,v指用户界面,c是控制器 使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式 是一种软件设计模式,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性...

m0_57426732的博客 1万+

学习海康VisionMaster之BLOB分析

6):软阈值(相对):是一种动态阈值分割方法,其核心思想是 基于图像局部特性(如背景或参考区域)自适应调整阈值范围,适用于光照变化明显、背景灰度不均的场景。3:拖入位置修正功能:这个非常重要,模版匹配成功后会得到新的匹配坐标,那和创建模版时候的原点坐标会组合成一个坐标变换矩阵,通过这个矩阵,那后续的blob分析窗口则可以实现精确定位来查找目标对象。(2):设定参数:这里阈值方式是blob的核心算法,根据不同的应用场景来选择不同的阈值方式。8:高级参数:是设定blob区域的特征范围,用于筛选出来特定的区域。

yuhouxiyang的专栏 3621

obisidian插件之【templater】模板的中文说明书

Templater 是一种模板语言,可让您将变量和函数结果插入到笔记中。它还允许您执行操作这些变量和函数的 JavaScript 代码。使用 Templater,您将能够创建强大的模板来自动执行手动任务。

m0_55795604的博客 1万+

Day57.表述层(MVC)、Thymeleaf: 逻辑视图、对象图(OGNL)、分支迭代...

Thymeleaf

a111042555的博客 1418

Arduino开发实例-EMG 肌肉信号传感器

在医学研究中,测量肌肉的活动、收缩和扩张非常重要。 EMG 肌肉传感器测量肌肉活动并产生信号以显示扩张和收缩量。 因此,输出取决于所选肌肉的活动量。 3 绿色、红色和黄色电极连接到模块,用于传输来自肌肉运动的电信号。

视觉与物联智能 1699

[Java面试]二:常见面试题之框架篇Spring MVC

我的博客首页:http://blog.youkuaiyun.com/m0_37116405 我的GitHub首页:https://github.com/KissLouis 什么是Spring MVC 1Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架。使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模...

Louis的博客 1万+

5、自适应 RBF 神经网络控制方法解析

本文详细解析了基于RBF神经网络的多种自适应控制方法,包括针对函数未知、参数未知以及存在外部干扰的非线性系统的控制策略。通过李雅普诺夫稳定性理论设计自适应律,确保闭环系统稳定,并结合仿真示例和MATLAB代码实现,展示了方法的有效性。文章还对比了不同控制方法的适用场景,并给出了实际应用流程,为复杂系统控制提供了系统性解决方案。

ol789012的博客 75

【机器人学】常用的小车底盘运动学模型推导(附C++代码)

认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长!本文先对小车底盘运动学模型介绍做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章提示:以下是本篇文章正文内容对于小车的分析,仅仅考虑XY轴的线速度和Z轴的角速度,不考虑和横滚和俯仰的速度,【防盗标记–盒子君hzj】也不考虑Z轴的高度变化【即:车底盘的运动可以用三个独立变量来描述:x轴平动、y轴平动、yaw轴转动】

qq_35635374的博客 1932

java开发中常用框架优缺点

Java Web 框架   一、Struts2是一个基于MVC设计模式的Web应用框架在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。 Struts1和Struts2 的原理对比:https://wenku.baidu.com/view/6cb3ca0810661ed9ad51f3d9.htmlStruts2优点1、实现了MVC模...

weixin_30319097的博客 949

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Struts2学习总结

一、概述 1、是什么? Struts2轻量级的MVC框架,主要解决了请求分发的问题,重心在控制层和表现层。低侵入性,与业务代码的耦合度很低。Struts2实现了MVC,并提供了一系列API,采用模式化方式简化业务开发过程。 1、运行在web层.负责处理请求的. 2、struts2已经帮我们封装了很多web中常用的功能(拦截器) 3、struts2 与 ...

Marmara 1万+

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Struts

Struts

偷懒的乌龟 211

Spring MVC 面经

1 什么是 Spring MVC? Spring MVC 是一个基于 Java 的实现了 MVC 设计模式的请求驱动类型的轻量级 Web 框架,通过把 Model,View,Controller 分离,将 web 层进行职责解耦,把复杂的 web 应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合 2 什么是 MVC 模型? 用户的请求会到达 Servlet,然后根据请求调...

Geffin的博客 846
上一篇: Python爬取百度百科页面数据
下一篇: 什么是“对用户友好”---转自王垠http://www.yinwang.org/blog-cn/2012/05/18/user-friendliness
github_zwl
博客等级 码龄11年 300粉丝 171原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值