深入解析Struts2中的OGNL表达式及其强大功能

OGNL表达式

老程序员的智慧:Struts框架的深度探索与实战指南

无敌全面Action与Servelet-参数接收-下载-上传一文全有


OGNL 是 Object Graphic Navigation Language(对象图导航语言)的缩写,是一个使用简单、功能强大的、开源的表达式语言,可以方便地 操作任何的对象属性、方法等。

Struts2 框架使用 OGNL 作为默认的表达式语言,主要用于页面的取值。它类似于 EL 表达式语言,但比 EL 语法强大很多。

EL Expression Language 表达式语言, 主要用来获取 JSP 页面四个域范围数据 (page、request、 session、 application )OGNL = EL +对象操作 ${name.toUpperCase()}

OGNL 表达式的三个要素

表达式 – OGNL 的表达式,编写表达式,OGNL 负责解析表达式,从而存值和取值

Root 对象 - 根对象

Context 对象 – OGNL 的上下文对象,运行的环境

TestOGNL

package com.igeek.ognl;
import org.junit.Test;
import com.igeek.domain.Customer;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;

public class TestOGNL {
	@Test
	public void testOgnl_01() throws OgnlException{
		//创建OgnlContext对象
		//通过ognl来操作对象的方法 
		OgnlContext context = new OgnlContext();
        //第一个参数表达式,第二个参数上下文,第三个是root
		Object obj =Ognl.getValue("'helloworld'.length()", context, context.getRoot());
		System.out.println(obj);
	}
	
	@Test
	public void testOgnl_02() throws OgnlException{
		//创建OgnlContext对象
		//通过ognl来获取map集合中的值
		OgnlContext context = new OgnlContext();
        //数据格式为Map
		context.put("name", "美美");
		//使用#key的方式获取
		Object obj =Ognl.getValue("#name", context, context.getRoot());
		System.out.println(obj);
	}
	
	@Test
	public void testOgnl_03() throws OgnlException{
		//创建OgnlContext对象
		//通过ognl来获取map集合中的值
		OgnlContext context = new OgnlContext();
		//创建一个客户对象
		Customer c = new Customer();
		c.setCust_name("冠希");
		c.setAge(18);
		//把客户放入root栈
		context.setRoot(c);
		//如果取root栈中的值,不需要家#
		Object obj =Ognl.getValue("age", context, context.getRoot());
		System.out.println(obj);
	}
}

OGNL 的用法详解

1.Java对象的直接访问

例如:<s:propertyvalue="'igeek'"/>

2.实例方法调用:访问对象的方法

语法:object.doSomething(),object是对象实例
例如:<s:propertyvalue="'igeek'.toUpperCase()"/>

3.静态方法调用(类的静态方法或静态变量的访问)

语法:@[类全名(包括包路径)]@[方法名|值名] 
例如:<s:propertyvalue="@java.lang.Math@max(10,21)"/>类的静态方法或静态变量的调用访问。
格式:@[类全名(包括包路径)]@[方法名|值名],如:@java.lang.Math@max(10,20)、@com.igeek.MyConstant@APP_NAME 
注意:必须通过配置struts2的常量来开启静态方法调用功能:
struts.ognl.allowStaticMethodAccess=true 

4.赋值操作或表达式串联。–可以运算

例如:<s:propertyvalue=*"1+2"*/>
OGNL可以访问上下文(OGNL Context)和ActionContext。(值栈相关访问-重要) 

OGNL.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

//引入标记库
<%@ taglib uri="/struts-tags" prefix="s"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h3>访问java对象</h3>
	<s:property value="'igeek'"/>
	<h3>操作对象的属性</h3>
	<s:property value="'igeek'.toUpperCase()"/>
	<h3>静态方法的访问  </h3>
	<h3>语法:@包名+类名@方法名(参数)</h3>
	<h3>需要开启静态方法访问</h3>
	<s:property value="@java.lang.Math@max(10,21)"/>
	<h3>类似EL表达式,进行数值运算</h3>
	<s:property value="1+2"/>
</body>
</html>

静态方法的调用,开启常量

struts.xml

<!-- 开启静态访问常量 -->
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>

值栈概述(掌握)

值栈(ValueStack)是什么

值栈(ValueStack),是 Struts2 的数据中转站,栈中自动保存了当前 Action 对象和其他相关对象(包括常用的 Web 对象的引用,如 request、session、application 等),也可以手动保存自己的数据对象,同时也可以随时随地将对象从值栈取出或操作(通过 OGNL 表达式)

值栈(ValueStack),实际是一个接口的对象的称呼,接口是 ValueStack 类,实现类是OgnlValueStack 类,该对象是 Struts2 利用 OGNL 的基础,或者说 Struts2 中 Ognl 使用都是基于值栈完成的。

Struts2 框架将 ValueStack 对象保存在 request 域中,键为“struts.valueStack”,即值栈是request 域中的一个对象,一个请求对应一个 Action 实例和一个值栈对象。

【示例 1】值栈的获取方式:

1) request.getAttribute(“struts.valueStack”):用的较少

2) ActionContext.getContext().getValueStack():用的非常多:底层使用的还是第一种方式获取ActionContext(Action 上下文,工具类)

ValueStackAction1 值栈的获取方式

package com.igeek_02_valuestack;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
public class ValueStackAction1 extends ActionSupport{
	@Override
	public String execute() throws Exception {
		//获取值栈
		//1.从request域中获取
		ValueStack vs1 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
		//2.通过ActionContext对象获取
		ValueStack vs2 = ActionContext.getContext().getValueStack();
		System.out.println(vs1 == vs2);
		System.out.println(vs1.hashCode());
		System.out.println(vs2.hashCode());
        //两种方式创建的 ValueStack 是一致的
        //每一次请求都会创建新的 ValueStack
		return NONE;
	}
}

ValueStackAction2 ValueStack 的存储结构

package com.igeek_02_valuestack;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
public class ValueStackAction2 extends ActionSupport{
	@Override
	public String execute() throws Exception {
		ValueStack vs = ActionContext.getContext().getValueStack();
		//往值栈中放数据
		vs.push("柳岩");
		vs.push("刘德华");
		vs.push("刘亦菲");
        
        
		//使用k-v结构存储值  推荐使用
		vs.set("name", "范冰冰");
		vs.set("age", 18);
		//总结:使用push和set方法都会把数据放入root栈中
		return NONE;
	}
}

ValueStackAction3 把数据放入map栈中

package com.igeek_02_valuestack;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;

public class ValueStackAction3 extends ActionSupport{
	@Override
	public String execute() throws Exception {
		ActionContext context = ActionContext.getContext();
		//把数据放入map栈中
		context.put("name", "柳岩");
		context.put("age", 20);
		return NONE;
	}
}

值栈的操作(掌握重要)

3.1 值栈的存值机制

【回顾知识了解】

栈的概念:栈是一种数据结构,它按照先进后出的原则存储数据,即先进入的数据被压入栈底,最后进入的数据在栈顶,需要读取数据的时候,从栈顶开始弹出数据(即最后一个数据被第一个读出来)。栈也被称为先进后出表,可进行插入和删除操作,插入称之为进栈(压栈)(push),删除称之为退栈(pop),允许操作的一端称为栈顶(top),另外一端就称为栈底(bottom)。栈底固定,而栈顶浮动。对于栈就只能每次访问它的栈顶元素。

3.2 值栈的取值机制

OgnlValueStack 类包含两个重要的属性,一个 root(CompoundRoot)和一个 context(OgnlContext)。OGNL Context 是 struts2 的数据中心,其结构示意图:

image-20230402204921409

在这个 OgnlContext 对象中,有一个默认的顶层对象 root(引用),俗称 root 栈,下面一个对象称之为 Map 栈。

对 OGNL 表达式的操作都是基于 OgnlContext(map 栈)对象,访问的规则如下:

如果访问 root 栈内容(CompoundRoot 对象栈内容), 不需要#,直接通过元素的名称来访问。

如果访问 Map 栈内容 (如 request、response、session、servletContext、attr、parameters), 需要#key 来引用访问,例如 #request.name 相当于request.getAttribute(“name” )

Action

package com.igeek_03_valuestack;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;

public class ValueStackAction1 extends ActionSupport{
	@Override
	public String execute() throws Exception {
		ValueStack vs1 = ServletActionContext.getContext().getValueStack();
		vs1.push("柳岩");
		vs1.push("刘亦菲");
		//通过k-v的形式把数据放入root栈
		vs1.set("name", "刘德华");
		//map栈放数据
		ActionContext.getContext().put("name", "张三");
		return SUCCESS;
	}
}

jsp取值

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h3>获取栈顶元素</h3>
	<s:property value="top"/> -- <s:property value="[0].top"/>
	<h3>获取栈中第二个元素</h3>
	<s:property value="[1].top"/>
	<h3>取root栈中k-v形式存的数据</h3>
	<s:property value="name"/>
	<h3>取map栈中的数据</h3>
	<s:property value="#name"/>
</body>
</html>

值栈的默认搜索

在实际开发中,使用 OGNL 表达式时,会经常用到值栈的默认搜索,即自动搜索值栈,找到所需要的值。

Action

package com.igeek_03_valuestack;

import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
public class ValueStackAction2 extends ActionSupport{
	@Override
	public String execute() throws Exception {
		//map栈放数据
		ActionContext.getContext().put("name", "张三");
		//root栈
		ServletActionContext.getContext().getValueStack().set("username", "柳岩");
		//map栈
		ActionContext.getContext().put("username", "刘亦菲");
		//从root栈中取数据
		String username1 = ActionContext.getContext().getValueStack().findString("username");
		//从map栈中取数据
		String username2 = ActionContext.getContext().getValueStack().findString("#username");
		System.out.println("root栈:"+username1);
		System.out.println("map栈:"+username2);
		
		return SUCCESS;
	}
}

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h3>获取map栈中的数据</h3>
    
	<h3>不加#的情况下:先搜索root栈,如果没有,再搜索map栈</h3>
	<h3>加#的情况下,直接搜索map栈</h3>
    
	*<h3>实际开发中,单个数据直接放入root栈,k-v形式的数据放入map栈</h3>
	<s:property value="name"/>
</body>
</html>

模型驱动优先属性驱动接收参数

【问题】为什么在赋值的时候,模型驱动的属性会优先赋值?模型驱动是如何具备更高的优先级别?

模型驱动获取参数优先于属性驱动

package com.igeek_03_valuestack;
public class User {
	private String username;
	private String password;

}

Action

package com.igeek_03_valuestack;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
public class ValueStackAction3 extends ActionSupport implements ModelDriven<User>{
	//模型驱动
	private User user = new User();
	//属性驱动
	private String username;
	@Override
	public User getModel() {
		return user;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	@Override
	public String execute() throws Exception {
		return SUCCESS;
	}
}

jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h3>s:debud标签可以在html页面上查看值栈内容</h3>
	<s:debug></s:debug>
</body>
</html>

Struts2使用表达式语言来操作值栈

Struts 2 支持以下几种表达式语言:

OGNL(Object-Graph Navigation Language),可以方便地操作对象属性的开源表达式语言;

EL(Expression Language),可以方便的操作 JSP 页面四个域范围数据 (page、 request、session、 application );

JSTL(JSP Standard Tag Library),JSP 2.0 集成的标准的表达式语言;

Groovy,基于 Java 平台的动态语言,它具有时下比较流行的动态语言(如 Python、Ruby和 Smarttalk 等)的一些起特性;

Velocity,严格来说不是表达式语言,它是一种基于 Java 的模板匹配引擎,据说性能要比 JSP 好。

Struts 2 默认的表达式语言是 OGNL,原因是它相对其它表达式语言更简单、强大,最重要的是可以直接操作值栈。

OGNL对Struts2的值栈的操作方式

明确一点:OGNL 表达式对值栈的操作,必须依赖 Struts2 的标签,所以每个 jsp 页面都需要引入 struts2 的标签库

Struts2 通过值栈存放数据,那如何取数据呢? 通过 Ognl 表达式访问值栈中的数据

在 Struts2 中,OGNL 表达式三种特殊符号的使用方式 :

# 号用法

OgnlAction1

package struts2_d04_c04;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class OgnlAction1 extends ActionSupport{
	@Override
	public String execute() throws Exception {
		//把数据放入request、session、application3个域中
		ServletActionContext.getRequest().setAttribute("name", "request数据");
		ServletActionContext.getRequest().getSession().setAttribute("name", "session数据");
		ServletActionContext.getServletContext().setAttribute("name", "application数据");
		return SUCCESS;
	}
}

ognl1.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h3>OGNL表达式获取request域中的数据</h3>
	<s:property value="#request.name"/> | ${requestScope.name}
	<h3>OGNL表达式获取session域中的数据</h3>
	<s:property value="#session.name"/> | ${sessionScope.name}
	<h3>OGNL表达式获取application域中的数据</h3>
	<s:property value="#application.name"/> | ${applicationScope.name}
	<h3>如何获取page域的数据</h3>
	<%
		pageContext.setAttribute("name", "page数据");
	%>
	<s:property value="#attr.name"/> | ${name}
	<h3>获取地址栏的信息</h3>
	<s:property value="#parameters.name"/>
</body>
</html>

% 号用法 强制解析

Action

package struts2_d04_c04;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class OgnlAction2 extends ActionSupport{
	@Override
	public String execute() throws Exception {
		ServletActionContext.getContext().getValueStack().set("username", "tom");
		return SUCCESS;
	}
}

jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h3>文本框 获取值栈中的值 ,使用%强制解析值栈中的值</h3>
	<s:textfield value="%{username}"></s:textfield>   //tom
	<h3>强制不解析</h3>
	<s:property value="%{'username'}"/>   //username
</body>
</html>

$ 号用法

此处的 不能理解为 e l 表达式,因为此处的 不能理解为 el 表达式,因为此处的 不能理解为el表达式,因为此处的必须用在 struts 特有的文件中,只能用在 struts2的文件中

用法:是用在 struts2 特有的文件中的 主要作用:允许我们在配置文件中使用 O G N L 表达式, ( 换句话说: 主要作用:允许我们在配置文件中使用 OGNL 表达式,(换句话说: 主要作用:允许我们在配置文件中使用OGNL表达式,(换句话说:可以在 xml 文件中获取值栈的值。)

配置文件主要指:struts.xml、国际化的文件(xxx.properties)、校验配置文件(xxx-validation.xml)。

OgnlAction3

package struts2_d04_c04;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;

public class OgnlAction3 extends ActionSupport{
	@Override
	public String execute() throws Exception {
		//如果要在xml或properties文件中获取值栈的值,值不能是中文
		ServletActionContext.getContext().getValueStack().set("productName", "iphone");
		return SUCCESS;
	}
}

messages.properties

productInfo=\u6B22\u8FCE\u4F7F\u7528${productName}\u4EA7\u54C1

struts.xml

<!-- 配置国际化常量 -->
<constant name="struts.custom.i18n.resources" value="messages"></constant>

jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h3>获取国际化文件中的值</h3>
	<s:text name="productInfo"></s:text>
</body>
</html>

第二种用法struts中配置

jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h3>获取URL中的参数</h3>
	<s:property value="#parameters.productName"/>
</body>
</html>

struts.xml

<action name="ognl4" class="struts2_d04_c04.OgnlAction4">
<!-- 获取值栈中的产品名称,当做参数传给页面 -->
<result type="redirect">/c_ognl/ognl4.jsp?productName=${productName}</result>
</action>

EL表达式获取值栈的数据

jsp

<body>
	<h3>EL表达式获取值栈中的值</h3>
	${productName}
</body>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值