Struts2(三)

本文详细介绍了Struts2中的OGNL表达式,包括其概述、基本用法、OGNL上下文(如ContextMap和ActionContext)、ValueStack对象的内部结构以及如何在页面上使用OGNL表达式获取数据。同时,提到了Struts2对EL表达式的改变,以及通过案例展示了如何优化客户列表的展示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. OGNL表达式

1.1 OGNL表达式概述

1.1.1 什么是OGNL表达式

OGNL的全称是对象图导航语言(Object-Graph Navigation Language),它是一种功能强大的开源表达式语言,使用这种表达式语言,可以通过某种表达式语法,存取Java对象的任意属性调用Java对象的方法,同时能够自动实现必要的类型转换。如果把表达式看作是一个带有语义的字符串,那么OGNL无疑成为了这个语义字符串与Java对象之间沟通的桥梁。

1.1.2 OGNL表达式的由来

它原本是xwork2中的默认表达式语言,当年OpenSymphony和apache在合作开发struts2框架时,把这个表达式也引进来了,所以就变成了struts2的默认表达式语言。

1.1.3 OGNL表达式的使用要求

要想使用ognl表达式,一般情况下都得需要使用struts2的标签库。

<%@ taglib uri="/struts-tags" prefix="s" %>

1.1.4 它的特点

它不仅可以用于取值,显示。还可以赋值。取值是我们程序员使用框架做的事情。赋值是框架为我们做的。

1.2 OGNL表达式的基本用法

1.2.1 s:property标签输出内容到浏览器

1.2.1.1 s:property的用法
<%--要想使用OGNL表达式获取数据,此时需要借助struts2的标签库
    s:property标签实现把数据输出到浏览器上
    <s:property value=""/>
    value属性的取值是一个OGNL表达式。
    标签会把value属性取值所对应的内容输出到浏览器上
    如果没有任何对应内容,则什么都不显示
--%>
OGNL的最基本用法:<s:property value="OGNLExpression"/>
1.2.1.2 OGNL表达式和字符串的转换
<%--OGNL表达式和字符串的转换
    表达式转成字符串
    %{''}  | %{""}
    可以把%{}去掉
 --%>
OGNL转成一个普通的字符串:<s:property value="%{'OGNLExpression1'}"/><br/>
OGNL转成一个普通的字符串:<s:property value='%{"OGNLExpression2"}'/><br/>
OGNL转成一个普通的字符串:<s:property value='"OGNLExpression3"'/><br/>
OGNL转成一个普通的字符串:<s:property value="'OGNLExpression4'"/><br/>

<!--
    字符串转成表达式
    %{}把字符串套起来
-->
<!-- user.name 看上去是字符串,当它执行user对象的getName方法时,表示用OGNL表达式解释。 -->
字符串转成一个OGNL表达式:<s:textfield name="username" value="%{user.name}"/>

image-20200607170212242

1.2.2 OGNL表达式访问对象的方法

<%--OGNL表达式访问对象的方法 --%>
调用字符串的长度方法:<s:property value="'OGNLExpression1'.length()"/><br/>
调用字符串的转大写方法:<s:property value="'OGNLExpression1'.toUpperCase()"/><br/>
调用字符串的分隔方法:<s:property value="'OGNLExpression1'.split('E')"/><br/>

1.2.3 OGNL表达式访问类的静态属性和静态方法

<%--OGNL表达式访问类的静态成员(静态属性)
   访问静态属性需要按照固定的书写规范来写。
   规范是:
      @包名.包名...类名@静态属性名称
--%>
OGNL表达式访问静态属性:<s:property value="@java.lang.Integer@MAX_VALUE"/>
<%--OGNL表达式访问类的静态方法
    访问静态方法需要按照固定的书写规范来写。
    规范是:
        @包名.包名...类名@静态方法名称
--%>
OGNL表达式访问静态方法:<s:property value="@java.lang.Math@random()"/>

1.2.4 OGNL表达式操作集合

1.2.4.1 list集合
<%--操作List集合
    s:radio标签的list取值就是一个OGNL表达式。
    {}就表示创建了一个List集合
    {'男','女'}===  List list = new ArrayList();  list.add("男"); list.add("女");
--%>
Struts2的单选按钮:<br/>
<s:radio list="{'男','女'}" name="gender2" label="性别"></s:radio>

HTML的单选按钮:<br/>
性别:<input type="radio" name="gender1" value="男">男
<input type="radio" name="gender1" value="女">女
1.2.4.2 map集合
<%--操作Map集合
    #{}就表示创建了一个Map集合。
    #{key:value,key:value}
    #{'male':'男','female':'女'}=== Map map = new HashMap();  map.put("male","男");  map.put("female","女");
--%>
Struts2的单选按钮:<br/>
<s:radio list="#{'male':'男','female':'女'}" name="gender4" label="性别"></s:radio>

HTML的单选按钮:<br/>
性别:<input type="radio" name="gender3" value="male">男
<input type="radio" name="gender3" value="female">女

2. OGNL上下文

2.1 ContextMap

2.1.1 ContextMap概述

它是OGNL上下文对象,是struts2中封装数据最大的对象。我们一次请求中所有用到的信息都可以在它里面找到。它是一个Map结构的对象,其中key是字符串,value是一个Object。

2.1.2 ContextMap中封装的数据

img

我们把这些内容拿出来逐个分析一下,得到下面的表格:

Map的key(类型是String)Map的Value (类型是Object)说明信息
applicationJava.util.Map<String,Object>封装的应用域中的所有数据
sessionJava.util.Map<String,Object>封装的会话域中的所有数据
requestJava.util.Map<String,Object>封装的请求域中的所有数据
valueStack(特殊)com.opensymphony.xwork2.ognl.OgnlValueStack它是List结构
parametersJava.util.Map<String,String[]>封装的是请求参数
attrJava.util.Map<String,Object>封装的是四大域的组合数据,从最小的域开始搜索
actioncom.opensymphony.xwork2.ActionSupport当前执行的动作类对象

image-20200607171436366

2.2 ActionContext

2.2.1 ActionContext对象概述

它是一个工具类,是struts2框架提供给我们的,可以让我们调用其中的方法,快速的操作ContextMap。用它操作OGNL上下文对象,比直接操作ContextMap要方便很多。

2.2.2 ActionContext对象与ContextMap的关系

ActionContext就相当于对ContextMap进行了一次再封装。

image-20200607173258169

2.2.3 ActionContext何时创建

由于ActionContext是操作的ContextMap,而ContextMap中封了我们一次请求的所有数据,所以它的创建应该是每次请求访问Action时,即核心控制器(StrutsPrepareAndExecuteFilter)的doFilter方法执行时,下图是代码截取:

img

2.2.4 ActionContext的线程安全

我们都知道,java的web工程是多线程的,那么每个线程在访问Action时,都会创建自己的ActionContext,那么是如何保证在获取ActionContext时,每个线程都能获取到自己的那个呢?

答案就是,每次创建ActionContext时,把对象绑定到当前线程上。下图是代码截取:

img

2.2.5 ActionContext的获取

使用ActionContext类中的静态方法getContext()从当前线程上获取

2.2.6 获取ContextMap中的数据

2.2.6.1 s:debug标签的使用
<%-- 引入标签库 --%>
<%@ taglib uri="/struts-tags" prefix="s" %>

<%--1、struts2的debug标签
    它是一个用于开发阶段的标签,查看我们OGNL上下文中内容的标签 --%>
<s:debug/>

2.2.6.2 使用OGNL表达式获取Map中的数据

动作类存数据:

/**
 * ActionContext的数据存取
 *
 * @author wgy
 */
public class Demo1Action extends ActionSupport {

    /**
     * 通过ActionContext往ContextMap中存入数据
     * 	    contextMap    hello context map
     *
     * 往应用域中存入数据:用两种方式实现
     * 	    applicationMap		hello application map
     * 	    applicationAttr		hello application attr
     *
     * 往会话域中存入数据:同上用两种方式
     * @return
     */
    public String demo1(){
        //1.获取ActionContext
        //从当前线程上获取
        ActionContext context = ActionContext.getContext();
        //2.存入数据
        context.put("contextMap", "hello context map");

        //3.往应用域中存入数据
        //第一种方式:使用原始ServletAPI对象ServletContext
        ServletContext applicationAttr = ServletActionContext.getServletContext();
        applicationAttr.setAttribute("applicationAttr", "hello application attr");

        //第二种方式:根据key从ActionContext中获取应用域的map,往map中存入数据
        Map<String,Object> applicationMap = context.getApplication();
        applicationMap.put("applicationMap","hello application map");

        //4.往会话域中存入数据
        //第一种:使用ServletAPI的HttpSession
        HttpSession session = ServletActionContext.getRequest().getSession();
        session.setAttribute("sessionAttr", "hello session attr");
        //第二种:获取key为session的map
        Map<String,Object> sessionMap = context.getSession();
        sessionMap.put("sessionMap","hello session map");

        return SUCCESS;
    }
}

在页面中使用OGNL表达式获取:

<%--借助struts2的s:property标签和OGNL表达式获取ActionContext存入的数据
    我们现在获取的数据,都是在map中。
    获取Map中的数据,OGNL表达式的写法:
        #key
        如果还想继续向下获取,使用.key的方式
--%>
<s:property value="#contextMap"/><br/>
<s:property value="#application.applicationMap"/><br/>
<s:property value="#session.sessionAttr"/>

<s:property value="#session.sessionMap"/>

2.3 ValueStack对象

2.3.1 ValueStack对象概述

ValueStack是Struts的一个接口,字面意义为值栈,OgnlValueStack是ValueStack的实现类,客户端发起一个请求struts2架构会创建一个action实例同时创建一个OgnlValueStack值栈实例,OgnlValueStack贯穿整个 Action 的生命周期。

它是ContextMap中的一部分,里面的结构是一个List,是我们可以快速访问数据一个容器。它的封装是由struts2框架完成的。

通常情况下我们是从页面上获取数据。它实现了栈的特性(先进后出)。

2.3.2 ValueStack的内部结构

在 OnglValueStack 中包含了一个CompoundRoot的对象,该对象继承了ArrayList,并且提供了只能操作集合第一个元素的方法,所以我们说它实现了栈的特性。同时,它里面定义了一个ContextMap的引用,也就是说,我们有值栈对象,也可以通过值栈来获取ContextMap。

image-20200607173453041

2.3.3 获取ValueStack中的数据

2.3.3.1 值栈中都有什么

首先我们要明确,值栈中存的都是对象。因为它本质就是一个List,List中只能存对象。

值栈中包含了我们通过调用push方法压栈的对象,当前执行的动作了和一个名称为DefaultTextProvider的类。值栈中的内容如下图:

img

2.3.3.2 在动作类中往值栈中存入数据
/**
 * ValueStack的数据存取
 *
 * @author wgy
 */
public class Demo2Action extends ActionSupport {

    //把私有成员放入值栈中
    private String name = "泰斯特";

    public String getName() {
        return name;
    }

    /**
     * 获取ValueStack,并且压栈操作
     * @return
     */
    public String demo2(){
        //1.获取ActionContext
        //从当前线程上获取
        ActionContext context = ActionContext.getContext();
        //2.获取ValueStack对象
        ValueStack vs = context.getValueStack();
        //3.压栈操作
        Student s = new Student("张三",18,"male");
        
        vs.push(s);
        return SUCCESS;
    }
}
2.3.3.3 我们可以获取值栈中的什么

一般情况下,我们都是根据debug标签中显示的Property Name来获取Property Value。

当然我们也可以获取栈顶对象。

image-20200607175025011

2.3.3.5 在页面上使用OGNL表达式获取数据
<%--获取值栈的数据也需要借助于struts2的标签库
    使用s:property获取
    获取值栈的数据,是直接写属性名称,得到的就是属性的值。
    OGNL表达式的找法,是从栈顶逐个属性名称开始查找,只要找到之后,就不再继续查找,而是返回结果。
 --%>
姓名:<s:property value="name"/><br/>
年龄:<s:property value="age"/><br/>
性别:<s:property value="gender"/><br/>

<%--获取指定位置的属性 --%>
获取第一个name:<s:property value="[0].name"/><br/>
获取第二个name:<s:property value="[1].name"/>

<%--如果使用s:property标签,没有写value属性,取的是栈顶对象 --%>
<s:property/>
2.3.3.6 OGNL表达式执行时调用的方法
<%--s:property在通过OGNL表达式获取数据时,所调用的方法:ValueStack中的findValue(String expr); --%>
<%
    ActionContext context = ActionContext.getContext();
    ValueStack vs = context.getValueStack();
    Object o1 = vs.findValue("[0].name");
    out.println(o1);
    out.println("<br/>");
    Object o2 = vs.findValue("[1].name");
    out.print(o2);
    out.println("<br/>");
    Object o3 = vs.findValue("#application.applicationMap");
    out.print(o3);
%>

3. Struts2中使用EL表达式

3.1 EL表达式回顾

EL表达式的写法:${表达式}。

它是从四大域中,由小到大逐个域搜索,根据名称获取值。只要找到了,就不再继续搜索。

它的原理:使用的是PageContext类中的findValue方法。

3.2 Struts2对EL表达式的改变

Struts2框架中对EL表达式做了如下改变:

EL表达式原来的搜素顺序: 
    page Scope——>request Scope——>session Scope——>application Scope
EL表达式改变后的搜索顺序:
    page Scope—>request Scope—>valueStack—>contextMap—>session Scope—>application Scope

struts2框架对request对象进行了包装,并且对getAttribute方法进行了增强,代码如下:

img

4. OGNL表达式中的各种符号总结

4.1 %

1、把OGNL表达式转成普通字符串 %{""}
2、把字符串转成OGNL表达式%{}

4.2 #

1、获取ContextMap中的数据。#key
2、在页面中可以创建Map集合。 #{}

4.3 $

1、EL表达式使用
2、可以在struts2的配置中使用OGNL表达式(配置可以是xml文件,也可以是注解)${}

5. 案例-优化客户列表的展示

5.1 改造Action

我们把之前查询所有客户的动作方法改造一下,之前我们是把查询结果存入请求域中了,而此时我们只需要在Action中定义一个集合,并且提供get/set方法,它就会出现在值栈中。就可以在页面中使用OGNL表达式获取。

/**
 * 查询所有客户
 * @return
 */
private List<Customer> customers;

public String findAllCustomer(){
    //1.调用service查询客户
    List<Customer> customers = customerService.findAllCustomer();
    //2.返回
    return "findAllCustomer";
}

public List<Customer> getCustomers() {
	return customers;
}

public void setCustomers(List<Customer> customers) {
	this.customers = customers;
}

5.2 改造jsp

在显示客户列表时,我们之前采用的是jstl标签库的c:forEach标签,今天我们将使用struts2提供的迭代标签s:iterator。

<%-- 
    <c:forEach items="${customers}" var="customer">
        <TR>
            <TD>${customer.custName }</TD>
            <TD>${customer.custLevel }</TD>
            <TD>${customer.custSource }</TD>
            <TD>${customer.custIndustry }</TD>
            <TD>${customer.custAddress }</TD>
            <TD>${customer.custPhone }</TD>
        </TR>
    </c:forEach> 
--%>

<%--
    struts2中的迭代标签:
    属性:
    value:它的取值是一个OGNL表达式
    var:写了该属性:它会把var的值作为key,把当前遍历的对象作为value,存入contextMap中
    没写该属性:它会把每次遍历的对象压入栈顶
--%>
<%-- 
    <s:iterator value="customers" var="cust">
    	<TR>
            <TD><s:property value="#cust.custName"/></TD>
            <TD><s:property value="#cust.custLevel"/></TD>
            <TD><s:property value="#cust.custSource"/></TD>
            <TD><s:property value="#cust.custIndustry"/></TD>
            <TD><s:property value="#cust.custAddress"/></TD>
            <TD><s:property value="#cust.custPhone"/></TD>                       
        </TR>
    </s:iterator> 
--%>

<s:iterator value="customers">
    <TR>
        <TD><s:property value="custName"/></TD>
        <TD><s:property value="custLevel"/></TD>
        <TD><s:property value="custSource"/></TD>
        <TD><s:property value="custIndustry"/></TD>
        <TD><s:property value="custAddress"/></TD>
        <TD><s:property value="custPhone"/></TD>
    </TR>
</s:iterator>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值