Skip to content

16. 示例 13 - 操作的上下文

本应用旨在展示操作可访问:

  • 请求参数
  • 请求的属性
  • 用户会话的属性

Image

16.1. NetBeans 项目

NetBeans 项目如下:

  • 在 [1] 中,视图 [Context.jsp]
  • 在 [2] 中,操作 [Action1.java] 和 Struts 配置文件 [example.xml]

16.2. Configuration

项目配置在 [example.xml] 中完成:


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
  <package name="example" namespace="/example" extends="struts-default">
    <action name="Action1" class="example.Action1">
      <result name="success">/example/Context.jsp</result>
    </action>
  </package>
</struts>
  • 第 8 行:对 URL [/example/Action1] 的请求将触发类 [example.Action] 的实例化。由于未指定具体方法,将执行方法 execute
  • 第9行:仅接受一个键。键 success 将导致显示视图 [Context.jsp]。

请求处理的简化架构如下:

该请求将由 Web 应用程序的两个组件处理:操作 [Action1] [1] 和视图 [Context.jsp] [2]。 这两个组件可以访问不同类型的数据:

  • 作用域数据 Application、[3]、c.a.d。这些数据对所有用户的所有请求均可见,且几乎始终为只读状态。 这些数据中通常包含应用程序的初始配置。在此,[Action1] 和 [Context.jsp] 具有访问这些数据的权限。
  • 作用域为 Session、[4]、c.a.d 的数据。这些数据对同一用户的所有请求均可见,且支持读写操作。 在此,[Action1]将使用读写权限的会话,而[Context.jsp]将使用只读权限的会话。
  • 作用域为 Requête 和 [5] 的数据,可供处理该请求的所有元素访问。在此,[Action1] 将数据写入该内存,而 [Context.jsp] 将从中读取数据。 请求作用域数据允许组件 N 向组件 N+1 传递信息。
  • 由客户端发送的请求参数 [6]。处理该请求的组件仅可对其进行只读操作。

16.3. 操作 [Action1]

类 [Action1] 的代码如下:


package example;

import com.opensymphony.xwork2.ActionSupport;
import java.util.Map;
import java.util.Set;
import org.apache.struts2.interceptor.ParameterAware;
import org.apache.struts2.interceptor.RequestAware;
import org.apache.struts2.interceptor.SessionAware;

public class Action1 extends ActionSupport implements SessionAware, RequestAware, ParameterAware {

  // 无参数构造函数
  public Action1() {
  }
  // 会话、请求、参数
  Map<String, Object> session;
  Map<String, Object> request;
  Map<String, String[]> parameters;

  @Override
  public String execute() {
    // 参数列表
    System.out.println("Paramètres...");
    Set<String> clés = parameters.keySet();
    for (String clé : clés) {
      for (String valeur : parameters.get(clé)) {
        System.out.println(String.format("[%s,%s]", clé, valeur));
      }
    }
    // 会话
    System.out.println("Session...");
    if (session.get("compteur") == null) {
      session.put("compteur", new Integer(0));
    }
    Integer compteur = (Integer) session.get("compteur");
    compteur = compteur + 1;
    session.put("compteur", compteur);
    System.out.println(String.format("compteur=%s", compteur));
    // 请求
    request.put("info1", "information1");
    // 页面显示JSP
    return SUCCESS;
  }

  // 会话
  public void setSession(Map<String, Object> session) {
    this.session = session;
  }

  // 请求
  public void setRequest(Map<String, Object> request) {
    this.request = request;
  }

  // 参数
  public void setParameters(Map<String, String[]> parameters) {
    this.parameters = parameters;
  }
}
  • 第 10 行:该类实现了以下接口
  • SessionAware:用于访问会话属性字典(第 16 行)。该接口仅有一个方法,即第 46 行中的方法。
  • RequestAware:用于访问请求属性字典(第17行)。该接口仅有一个方法,即第51行中的方法。
  • ParameterAware:用于访问查询参数字典(第 18 行)。 需要注意的是,每个键(即参数名称)对应一个值数组。这是为了处理那些提交多个值的输入字段,例如多选列表。接口 ParameterAware 仅有一个方法,即第 56 行中的方法。
  • 第 21 行:方法 execute,该方法在调用操作 [Action1] 时执行。当其执行时,拦截器已完成其工作:
  • 方法 setParameters(第 56 行)已被调用,且第 18 行的 parameters 字典包含请求的所有参数。
  • 方法 setSession(第 46 行)已被调用,第 16 行的 session 字典包含会话的所有属性。
  • 方法 setRequest(第 51 行)已被调用,第 17 行的 request 字典包含请求的所有属性。
  • 第 31-38 行:将与键 compteur 关联的值写入会话
  • 第 32-34 行:在会话中查找键 compteur。如果未找到,则将其与整数值 0 关联并存入会话。
  • 第 35-37 行:在会话中查找键 compteur,将其值递增,然后将该键重新存入会话。
  • 第 38 行:显示与键 compteur 关联的值。由于每次对操作 [Action1] 进行请求时都会进行递增,因此随着请求的进行,计数器的值应逐渐增加。
  • 第 40 行:在请求属性字典中插入一个键为 info1、值为 information1 的属性。请求的属性与参数不同。参数是由 Web 应用程序的客户端发送的。 而请求属性则用于处理该请求的 Web 应用程序中不同组件之间的通信。因此,在执行 [Action1] 之后,将显示视图 [Context.jsp]。我们将看到它能够获取请求的属性。
  • 第42行:方法execute返回键succes。

16.4. 消息文件

文件 [messages.properties] 内容如下:


Context.titre=Contexte de l''action
Context.message=Contexte de l''action
Context.parameters=Param\u00E8tres de l''action
Context.session=Elements de session
Context.request=Attributs de requ\u00EAte

16.5. 视图 [Context.jsp]

视图 [Context.jsp] 的作用是显示:

  • 查询的某些参数
  • 会话中 compteur 键的值
  • 查询中 info1 键的值

其代码如下:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
  <head>
    <title><s:text name="Context.titre"/></title>
    <s:head/>
  </head>

  <body background="<s:url value="/ressources/standard.jpg"/>">
    <h2><s:text name="Context.message"/></h2>
    <h3><s:text name="Context.parameters"/></h3>
    <s:iterator value="#parameters['nom']" var="nom">
      nom : <s:property value="nom"/><br/>
    </s:iterator>
    <s:iterator value="#parameters['prenom']" var="prenom">
      prenom : <s:property value="prenom"/><br/>
    </s:iterator>
    <s:iterator value="#parameters['age']" var="age">
      âge : <s:property value="age"/><br/>
    </s:iterator>
    <h3><s:text name="Context.session"/></h3>
    compteur : <s:property value="#session['compteur']"/>
    <h3><s:text name="Context.request"/></h3>
    info1 : <s:property value="#request['info1']"/>
  </body>
</html>
  • 第 12-14 行:显示与参数 nom 相关的所有值
  • 第 15-17 行:显示与参数 prenom 相关的所有值
  • 第 18-20 行:显示与参数 age 相关的所有值
  • 第22行:显示会话中与键compteur关联的值
  • 第 24 行:显示查询中与键 info1 关联的值

16.6. 测试

  • 在 [1] 中,Action1 被调用时未带参数
  • 在 [2] 中,[Context.jsp] 未找到参数
  • 在 [3] 中,[Context.jsp] 在会话中找到了键 compteur
  • 在 [4] 中,[Context.jsp] 在请求中找到了键 info1

再进行一次测试:

  • 在 [1] 中,Action1 被调用,其参数
  • 在 [2] 中,[Context.jsp] 显示这些参数
  • 在 [3] 中,[Context.jsp] 在会话中找到了密钥 compteur。计数器确实增加了 1,这表明两次请求之间确实进行了缓存。
  • 在 [4] 中,[Context.jsp] 在请求中找到了键 info1

需要提醒的是,方法 [Action1.execute] 会在 Web 服务器的控制台上输出信息。以下是一个示例:

1
2
3
4
5
6
7
Paramètres...
[prenom,y]
[prenom,y2]
[age,z]
[nom,x]
Session...
compteur=2

16.7. Conclusion

请记住以下几点:

  • 若需存储供所有用户所有请求共享的信息,将使用应用程序内存。我们稍后将展示一个示例。
  • 为了存储供同一用户的所有请求共享的信息,将使用该用户的会话。
  • 为了存储由处理同一请求的所有组件共享的信息,将使用请求内存。