GNL表达式入门
OGNL表达式概述什么是OGNL表达式
OGNL的全称是对象图导航语言(Object-Graph Navigation Language),它是一种功能强大的开源表达式语言,使用这种表达式语言,可以通过某种表达式语法,存取Java对象的任意属性,调用Java对象的方法,同时能够自动实现必要的类型转换。如果把表达式看作是一个带有语义的字符串,那么OGNL无疑成为了这个语义字符串与Java对象之间沟通的桥梁。
OGNL表达式的由来:
它原本是xwork2中的默认表达式语言,当年OpenSymphony和apache在合作开发struts2框架时,把这个表达式也引进来了,所以就变成了struts2的默认表达式语言。
结论:在struts2中,ognl表达式就是默认的表达式语言。
OGNL表达式的使用要求:
要想使用ognl表达式,一般情况下都得需要使用struts2的标签库。
例如:
struts2: <s:textfield name="username" lable="用户名"/>
html: 用户名:<input type="text" name="username"/>
细节:
struts2框架本身有可能把html中的某些字符串看成是ognl表达式。用来给属性赋值。
OGNL表达式的基本用法
借助s:property标签输出内容到浏览器
s:property的作用
写法:
<%--导入标签库--%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<%--要想使用OGNL表达式,需要借助struts2的标签--%>
<s:property value="OGNLExpression"/>
作用:
把value属性取值所对应的内容输出到浏览器上。注意:它不是把value的值输出到浏览器上。
属性:
value:取值是一个OGNL表达式。
运行结果:
此时浏览器不会有任何内容显示。究其原因,我们先要理解【把value属性取值所对应的内容输出到浏览器上】这句话。这句话中有【所对应的内容】这几个字,这几个字就说明它要去某个地方找数据,那自然是找到了就显示,找不到就什么都不显示了。
把一个OGNL表达式看成字符串的方式
把一个OGNL表达式看成是字符串的方式是:
%{'OGNL表达式'}或者是%{"OGNL表达式"}
到底是使用单引号还是双引号,是由外层引号决定的。以上两种都可以实现。
我们也可以简写,就是把外面的%{}去掉,直接用引号括住表达式。
'OGNL表达式'或者是"OGNL表达式"
示例代码:
<s:property value="%{'OGNLExpression'}"/>
<s:property value='%{"OGNLExpression1"}'/>
把一个字符串看成OGNL表达式的方式
把一个字符串看成是OGNL表达式的方式是: %{字符串}
OGNL表达式调用普通方法
OGNL表达式的强大之处在于它可以让我们在表达式中访问对象的方法,例如下面的代码:
长度:<s:property value="'OGNLExpression'.length()"/><br/>
转大写:<s:property value="'OGNLExpression'.toUpperCase()"/><br/>
分隔:<s:property value="'OGNLExpression'.split('E')"/><br/>
OGNL表达式访问静态属性和静态方法
OGNL表达式还支持访问静态成员,这其中包括静态属性和静态方法,但是必须按照提供的格式编写,格式是:@包名.包名...类名@静态属性名称。这其中...的含义表示有几级包,就写几个包名。
示例代码如下:
int的最大值:<s:property value="@java.lang.Integer@MAX_VALUE"/>
<%--但是会议用OGNL表达式访问静态方法,需要开启对静态方法访问的支持。
struts2框架默认不支持OGNL表达式的静态方法调用,开启的方式如下:
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
--%>
随机数:<s:property value="@java.lang.Math@random()"/>
OGNL表达式创建集合
list集合
用HTML在浏览器上输出一个单选性别:
<input type="radio" name="gender" value="男"/>男
<input type="radio" name="gender" value="女"/>女
<br/>
用Struts2的单选按钮标签输出一个单选
<%--s:radio用于在浏览器上显示一个单选按钮
list属性取值是一个OGNL表达式
{}就表示创建了一个List集合 List list = new ArrayList();
{'男','女'}
list.add("男");list.add("女");
--%>
<s:radio name="gender" list="{'男','女'}" label="性别"/>
map集合
用HTML在浏览器上输出一个单选性别:Map结构
<input type="radio" name="gender" value="male"/>男
<input type="radio" name="gender" value="female"/>女
<br/>
<%--#{}就表示创建了一个Map
里面的写法
#{'key':'value','key':'value'......}
--%>
<s:radio name="gender" list="#{'male':'男','female':'女'}" label="性别"/>
ContextMap
ContextMap概述
它是OGNL上下文对象,是struts2中封装数据最大的对象。我们一次请求中所有用到的信息都可以在它里面找到。它是一个Map结构的对象,其中key是字符串,value是一个Object。
ContextMap中封装的数据
下图是我们在struts2官方文档中找到的,请以此为准:
我们把这些内容拿出来逐个分析一下,得到下面的表格:
Map的key(类型是String) | Map的Value (类型是Object) | 说明信息 |
application | Java.util.Map<String,Object> | 封装的应用域中的所有数据 |
session | Java.util.Map<String,Object> | 封装的会话域中的所有数据 |
request | Java.util.Map<String,Object> | 封装的请求域中的所有数据 |
valueStack(特殊) | com.opensymphony.xwork2.ognl.OgnlValueStack | 它是List结构 |
parameters | Java.util.Map<String,String[]> | 封装的是请求参数 |
attr | Java.util.Map<String,Object> | 封装的是四大域的组合数据,从最小的域开始搜索 |
action | com.opensymphony.xwork2.ActionSupport | 当前执行的动作类对象 |
用颜色把它区分开,目的是让我们明确一件事:
蓝色的:是我们已经会了的。
四大域对象在jsp那天就已经学过了。
绿色的:是我们不用管的。
parameters:现在我们已经会用模型驱动了,所以再也不用自己取了。
attrs:由于ContextMap中已经包含了三大域,page域的范围又太小了。
action:在后面我们讲的值栈中,会提供这个当前执行的动作类。
红色的:它是我们第一次接触的,以前不会。而且是struts2中经常用的,所以它是重点!
我们关心的部分,结构如下图所示:
ActionContext
ActionContext对象概述
它是一个工具类,是struts2框架提供给我们的,可以让我们调用其中的方法,快速的操作ContextMap。用它操作OGNL上下文对象,比直接操作ContextMap要方便很多。
ActionContext对象以及和ContextMap的关系
ActionContext就相当于对ContextMap进行了一次再封装。
ActionContext何时创建
由于ActionContext是操作的ContextMap,而ContextMap中封了我们一次请求的所有数据,所以它的创建应该是每次请求访问Action时,即核心控制器(StrutsPrepareAndExecuteFilter)的doFilter方法执行时,
ActionContext的线程安全
我们都知道,java的web工程是多线程的,那么每个线程在访问Action时,都会创建自己的ActionContext,那么是如何保证在获取ActionContext时,每个线程都能获取到自己的那个呢?
答案就是,每次创建ActionContext时,把对象绑定到当前线程上。
这是ActionContext类中的方法:
ActionContext的获取
使用ActionContext类中的静态方法,如下图所示:
图中的actionContext到底是什么数据类型呢?
既然说ActionContext是对ContextMap的再封装,那么它是怎么封装的呢?
获取ContextMap中的数据
s:debug标签的使用
<%-- 引入标签库 --%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<%--1、struts2的debug标签
它是一个用于开发阶段的标签,查看我们OGNL上下文中内容的标签 --%>
<s:debug/>
使用OGNL表达式获取Map中的数据
获取Map中的数据,包括ContextMap,以及三大域中的数据。
我们首先往Map和三大域中存入一些数据:
/**
* 对ContextMap
* 和
* 三大域(ServletContext,HttpSession,ServletRequest)
* 的存值操作
*/
public class Demo1Action extends ActionSupport {
/**
* Map部分的存值操作
* @return
*/
public String demo1(){
//1.获取ActionContext对象 他就是一个Map结构
ActionContext context = ActionContext.getContext();//从当前线程上获取ActionContext对象
//2.往Map存入数据
context.put("contextMap", "hello context map");
//3.获取key为application的Map对象
//第一种方式:使用map操作,叫解耦的方式,不依赖原始ServletAPI对象
Map<String,Object> applicationMap = context.getApplication();//(Map<String, Object>) context.get("application");
applicationMap.put("applicationMap", "hello application map");
//第二种方式:使用原始ServletAPI对象操作,方便的方式。
ServletContext application = ServletActionContext.getServletContext();
application.setAttribute("applicationAttr", "hello application attr");
return SUCCESS;
}
}
在页面中使用OGNL表达式获取:
<%--2、使用OGNL表达式获取Map中的数据 需要借助s:property
获取map的数据:都是根据Key获取value,所以我们要提供的是key。
如何表示key:使用#,后面的内容就表示key
写法:
#key
--%>
获取ContextMap中的数据:<s:property value="#contextMap"/>
<hr/>
<%--3、使用OGNL表达式获取三大域中的数据
也需要使用s:property标签
写法就是
#key.key
--%>
获取key为application的map中的数据
<s:property value="#application.applicationMap"/><br/>
<s:property value="#application.applicationAttr"/>
ValueStack对象
ValueStack对象概述
ValueStack是Struts的一个接口,字面意义为值栈,OgnlValueStack是ValueStack的实现类,客户端发起一个请求struts2架构会创建一个action实例同时创建一个OgnlValueStack值栈实例,OgnlValueStack贯穿整个 Action 的生命周期。
它是ContextMap中的一部分,里面的结构是一个List,是我们可以快速访问数据一个容器。它的封装是由struts2框架完成的。
通常情况下我们是从页面上获取数据。它实现了栈的特性(先进后出)。
ValueStack的内部结构
在 OnglValueStack 中包含了一个CompoundRoot的对象,该对象继承了ArrayList,并且提供了只能操作集合第一个元素的方法,所以我们说它实现了栈的特性。同时,它里面定义了一个ContextMap的引用,也就是说,我们有值栈对象,也可以通过值栈来获取ContextMap。
获取ValueStack中的数据
值栈中都有什么
首先我们要明确,值栈中存的都是对象。因为它本质就是一个List,List中只能存对象。
值栈中包含了我们通过调用push方法压栈的对象,当前执行的动作了和一个名称为DefaultTextProvider的类
在我们不操作值栈时,默认的栈顶对象是当前执行的动作类。
在动作类中往值栈中存入数据
/**
* 对ContextMap中ValueStack的压栈操作
*/
public class Demo2Action extends ActionSupport {
private String name = "呵呵";
/**
* 往ValueStack中压栈
* @return
*/
public String demo2(){
//1.获取ActionContext对象
ActionContext context = ActionContext.getContext();
//2.获取ValueStack对象
ValueStack vs = context.getValueStack();
//3.压栈操作
Student s = new Student();
s.setName("泰斯特");
s.setAge(18);
vs.push(s);
return SUCCESS;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Student类:
/**
* 一个学生的模型
*/
public class Student implements Serializable {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
我们可以获取值栈中的什么
一般情况下,我们都是根据debug标签中显示的Property Name来获取Property Value。
当然我们也可以获取栈顶对象。
如何让Action中定义的成员出现在值栈中
在Action定义一个私有属性,并且提供公有get/set方法,那么该属性就会出现在值栈的Property Name中。显示的名称是根据get/set方法后面的内容决定的,与私有成员变量名称叫什么无关。
在页面上使用OGNL表达式获取数据
<%--1、获取值栈中的数据 需要借助struts2的标签s:property
明确的事情:
1、获取值栈中的数据,我们只能根据值栈中对象的property name获取property value。
2、获取值栈中的数据,都是直接写属性名称,获取的就是值,并不需要使用任何符号。
它是把value属性的取值作为值栈中对象的property name,在值栈中从栈顶逐个查找,只要找到了就返回结果,并且不再继续查找。
--%>
<s:property value="name"/><br/>
<s:property value="age"/>
<hr/>
<%--2、默认的栈顶对象是:当前执行的动作类
获取栈顶对象的方式:
使用s:property标签,不要写value属性
--%>
<s:property/>
<hr/>
<%--3、获取指定位置的对象中的属性值 --%>
<s:property value="[0].name"/><br/>
<s:property value="[1].name"/>
OGNL表达式执行时调用的方法
<%--4、ValueStack中的findValue方法 --%>
<%
ActionContext context = ActionContext.getContext();
ValueStack vs = context.getValueStack();
//其实之前的所有OGNL的操作,最终调用的方法都是findValue.
//Object value = valueStack.findValue(String expression);
Object o1 = vs.findValue("name");
out.print("findValue方法输出的:"+o1+"<br/>");
Object o2 = vs.findValue("[1].name");
out.print("findValue方法输出的:"+o2+"<br/>");
Object o3 = vs.findValue("#application.applicationMap");
out.print("findValue方法输出的:"+o3+"<br/>");
%>