Skip to content

8. 示例 06 – 会话

8.1. 会话的概念

当客户端浏览器首次连接到 Web 应用程序时,它会收到一个会话令牌——这是一串唯一的字符,客户端浏览器在向 Web 应用程序发送的每次新请求中都会将其一并发送回去。这使得 Web 应用程序能够识别该客户端浏览器。随后,Web 应用程序可以将数据与该会话令牌相关联。这些数据专属于单个客户端浏览器。因此,随着客户端浏览器发出请求,系统会逐步构建起数据存储。

如上所述,每个用户(浏览器)都有自己的存储空间,称为会话。该存储空间由同一用户的所有请求共享。此外,还有一种更高层次的存储空间,称为应用程序存储空间。该存储空间由所有用户的所有请求共享,通常为只读模式。

8.2. NetBeans 项目

[example-06] 项目是通过复制 [example-05] 项目创建的。我们将修改其中几个元素,以

  • 以充分利用用户会话。
  • 添加一个新操作 [Clear] [1] 来清空输入字段。

8.3. 配置

[struts.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>
  <!-- internationalization -->
  <constant name="struts.custom.i18n.resources" value="messages" />
  <!-- default package -->
  <package name="default" namespace="/" extends="struts-default">
    <default-action-ref name="index" />
    <action name="index">
      <result type="redirectAction">
        <param name="actionName">Saisir</param>
        <param name="namespace">/actions</param>
      </result>
    </action>
  </package>
  <!-- equity package -->
  <package name="actions" namespace="/actions" extends="struts-default">
    <action name="Saisir">
      <result name="success">/vues/Saisie.jsp</result>
    </action>
    <action name="Confirmer" class="actions.Confirmer">
      <result name="success">/vues/Confirmation.jsp</result>
    </action>
    <action name="Effacer" class="actions.Effacer">
      <result name="success">/vues/Saisie.jsp</result>
    </action>
  </package>
</struts>

第 27–29 行定义了一个与类 [Delete] 相关联的新操作 [Delete]。该操作的响应是视图 [Input.jsp]。

8.4. [Confirm] 操作

其演变过程如下:


package actions;
 
import com.opensymphony.xwork2.ActionSupport;
import java.util.Map;
import org.apache.struts2.interceptor.SessionAware;
 
public class Confirmer extends ActionSupport implements SessionAware{
 
  // model
  private String nom;
  // session
  private Map<String, Object> session;
 
  // getters and setters
 
  public String getNom() {
    return nom;
  }
 
  public void setNom(String nom) {
    this.nom = nom;
  }
 
  @Override
  public void setSession(Map<String, Object> session) {
    this.session=session;
  }
 
  @Override
  public String execute(){
    // put the name in the session
    session.put("nom",nom);
    // navigation
    return SUCCESS;
  }
 
}
  • 第 7 行:[Confirm] 类实现了 SessionAware 接口。该接口仅有一个方法,即第 25–27 行中的 setSession 方法。在调用 execute 方法之前,某个请求拦截器会通过 setSession 方法,将用户的会话以 Map<String, Object> 字典的形式注入(第 25 行)。 我们选择将此字典存储在第 12 行的 session 字段中。
  • 第 30–34 行:Action 的 `execute` 方法。当该方法执行时,session 字段已由某个拦截器初始化,name 字段则由另一个拦截器初始化。我们使用这个会话字典来存储 name 字段。因此,name 将成为用户会话的一部分,并在用户的所有请求中可用。

8.5. [Confirmation.jsp] 和 [Saisie.jsp] 视图

[Confirmation.jsp] 视图保持不变。[Saisie.jsp] 视图的更改如下:


<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title><s:text name="saisie.titre1"/></title>
  </head>
  <body>
    <h1><s:text name="saisie.titre2"/></h1>
    <s:form action="Confirmer">
      <s:textfield key="saisie.libelle" name="nom" value="%{#attr['nom']}"/>
      <s:submit key="saisie.valider" action="Confirmer"/>
      <s:submit key="saisie.effacer" action="Effacer"/>
    </s:form>
  </body>
</html>
  • 第 12 行:我们在 `<s:textfield>` 标签中添加了一个 `value` 属性该属性用于设置输入字段中显示的值。如果省略此属性,`value` 的默认值为 `name`。此处的属性值为一个 OGNL(对象图导航语言)表达式,格式为 `%{expression_to_evaluate}`。 此处的待评估表达式为 #attr['name']。系统将按以下顺序在当前操作、页面、请求、会话和应用程序中搜索 name 属性:当前操作、页面、请求、会话、应用程序。由于 [Confirm] 操作将 name 属性存储在会话中,因此该属性将在会话中被找到。如下请求所示:

在 [1] 中,输入的名称为 ST。我们知道 [Confirm] 操作已将该名称存入会话。点击链接 [2] 将跳转至 URL [3]。此时显示 [Saisie.jsp] 视图。对于输入字段,属性 %{#attr['name']} 会从会话中检索该名称。

  • 第 14 行:[清除] 按钮,该按钮会触发 [Clear] 操作的执行并显示 [Saisie.jsp] 视图

<action name="Effacer" class="actions.Effacer">
      <result name="success">/vues/Saisie.jsp</result>
</action>

8.6. [删除] 操作

[删除] 操作的代码如下:


package actions;
 
import com.opensymphony.xwork2.ActionSupport;
import java.util.Map;
import org.apache.struts2.interceptor.SessionAware;
 
public class Effacer extends ActionSupport implements SessionAware{
 
  // session
  private Map<String, Object> session;
 
  @Override
  public String execute(){
    // retrieve the name from the session
    String nom=(String)session.get("nom");
    // remove it from the session if necessary
    if(nom!=null){
      session.remove("nom");
    }
    // navigation
    return SUCCESS;
  }
 
  @Override
  public void setSession(Map<String, Object> map) {
    this.session=map;
  }
}

  • 第 7 行:[Delete] 类实现了 [SessionAware] 接口,这与 [Confirm] 操作一样。
  • 第 13 行:[Delete] 操作必须清除 [Input.jsp] 视图中 name 输入字段的内容。我们知道该视图是从会话中获取此 name 的。因此,我们必须从会话中移除该 name。这就是 execute 方法所做的工作。

让我们看看它是如何工作的:

在[1]中,我们希望清空输入字段。我们点击[清空]按钮。


<s:submit key="saisie.effacer" action="Effacer"/>

[清除] 操作将被执行。在 [2] 中,我们注意到调用的 URL 是 [确认] 操作的 URL。这是由于表单中的 <s:form> 标签所致:


<s:form action="Confirmer">

这将导致表单提交至 [Confirm] 操作。当点击 [Clear] 按钮时,参数

action:Delete=Delete

被发送到 URL [/actions/Confirm.action]。Struts 使用此参数来处理由 [Delete] 操作提交的数据。该操作会删除会话名称。[Input.jsp] 页面是 [Delete] 操作的响应:


<action name="Effacer" class="actions.Effacer">
      <result name="success">/vues/Saisie.jsp</result>
</action>

此操作会显示会话名称,随后显示一个空字符串 [3]。

我们编写了几个简单的示例,以介绍 Struts 2 的重要概念:

  • 页面国际化
  • 将提交的参数注入到 Action 字段中
  • 会话的概念
  • Action 与 View 之间的关系

既然我们已经掌握了这些概念,就可以着手处理更复杂的示例了。我们将首先介绍表单中可以使用的各种标签。