Skip to content

8. 示例 06 – 会话

8.1. 会话的概念

当客户端浏览器首次连接到 Web 应用程序时,它会收到一个会话令牌,即一串唯一的字符,并在向 Web 应用程序发送每次新请求时将其返回。这使得 Web 应用程序能够识别该客户端浏览器。随后,Web 应用程序可以将数据与该会话令牌相关联。这些数据仅属于该唯一的客户端浏览器。 因此,随着客户端浏览器的请求不断进行,会形成一个数据缓存。

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

8.2. NetBeans 项目

项目 [exemple-06] 是通过复制项目 [exemple-05] 获得的。我们将修改一些内容,以便

  • 利用用户的会话。
  • 添加新操作 [Effacer] [1] 以清空输入字段。

8.3. Configuration

文件 [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>
  <!-- 国际化 -->
  <constant name="struts.custom.i18n.resources" value="messages" />
  <!-- 默认包 -->
  <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>
  <!-- 操作包 -->
  <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 行定义了一个新的操作 [Effacer],该操作关联到类 [Effacer]。此操作的响应是视图 [Saisie.jsp]。

8.4. 操作 [Confirmer]

其演变如下:


package actions;

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

public class Confirmer extends ActionSupport implements SessionAware{
  
  // 模型
  private String nom;
  // 会话
  private Map<String, Object> session;
  
  // 获取器和设置器

  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(){
    // 将名称放入会话中
    session.put("nom",nom);
    // 导航
    return SUCCESS;
  }
  
}
  • 第 7 行:类 [Confirmer] 实现了接口 SessionAware。该接口仅有一个方法,即第 25-27 行中的方法 setSession。 在调用方法 execute 之前,请求拦截器之一将通过方法 setSession,以 Map<String, Object> 字典的形式注入用户会话(第 25 行)。 我们选择将该字典存储在第 12 行的 session 字段中。
  • 第 30-34 行:操作的 execute 方法。 当该方法执行时,字段 session 已被其中一个拦截器初始化,而字段 nom 则被另一个拦截器初始化。 我们使用该字典 session 来存储字段 nom。这样,该名称将成为用户内存的一部分,并可供用户的所有请求使用。

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 行:我们在 value 标签中引入了一个 value 属性。该属性用于设定在输入框中显示的值。 若未设置此属性,则 value = name。此处,属性的值是一个 OGNL(对象图导航语言)表达式,形式为 %{expression_à_évaluer} 形式的 OGNL 表达式。此处待求值的表达式为 #attr['nom']。系统将按当前操作、页面、请求、会话、应用程序的顺序查找 nom 属性。 由于操作 [Confirmer] 将属性 nom 放入会话中,因此该属性将在会话中被找到。下述请求即说明了这一点:

在 [1] 操作中,输入的名称是 ST。 我们知道操作 [Confirmer] 将该名称放入了会话中。链接 [2] 引导我们访问 URL [3]。 此时显示的是视图 [Saisie.jsp]。对于输入字段,属性 %{#attr['nom']} 可用于从会话中检索该名称。

  • 第 14 行:按钮 [Effacer] 将触发操作 [Effacer] 的执行,并显示视图 [Saisie.jsp]

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

8.6. 操作 [Effacer]

操作 [Effacer] 的代码如下:


package actions;

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

public class Effacer extends ActionSupport implements SessionAware{
  
  // 会话
  private Map<String, Object> session;
  
  @Override
  public String execute(){
    // 从会话中获取用户名
    String nom=(String)session.get("nom");
    // 如有需要,将其从会话中移除
    if(nom!=null){
      session.remove("nom");
    }
    // 导航
    return SUCCESS;
  }

  @Override
  public void setSession(Map<String, Object> map) {
    this.session=map;
  }
}
  • 第 7 行:类 [Effacer] 实现了接口 [SessionAware],与操作 [Confirmer] 一样。
  • 第 13 行:操作 [Effacer] 必须清除视图 [Saisie.jsp] 中名称输入字段的内容。 我们知道该视图会从会话中获取该名称。因此,必须从会话中移除该名称。这就是方法 execute 所做的工作。

让我们看看效果如何:

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


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

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


<s:form action="Confirmer">

该标签导致表单被提交至操作 [Confirmer]。点击按钮 [Effacer] 时,参数

action:Effacer=Effacer

被提交至 URL [/actions/Confirmer.action]。Struts 使用该参数将数据处理交由操作 [Effacer] 处理。该操作会移除会话名称。 页面 [Saisie.jsp] 是动作 [Effacer] 的响应:


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

该页面原本应显示会话名称,但此时却显示了一个空字符串 [3]。

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

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

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