struts2学习笔记

应项目要求需要使用struts。以下是学习笔记

Struts2 框架的优点

了解了这几个主要的优点,会促使你考虑使用Struts2 :

  • POJO表单及POJO操作 - Struts2 去除掉了Struts框架中的Action Forms部分。在Struts2框架下,你可以用任何一POJO来接收表单输入,同样的,你可以把任一POJO视为一个Action类。
  • 标签支持 - Struts2 改进了标签表单,而新的标签可让开发人员减少代码编写量。
  • AJAX支持 - Struts2 被认可接收进Web 2.0技术,并创建了功能非常类似于标准的Struts2 标签的AJAX标签,把AJAX支持整合进其结果中。
  • 易于整合 - Struts有多种整合方式可使用,现在与其他类型的框架,如Spring、Tiles、SiteMesh之类的,整合更为容易了。
  • 模板支持 - 支持使用模板生成视图。
  • 插件支持 - 有大量的插件可用于Struts2,而使用插件可以增强和扩大Struts2 核心行为。
  • 性能分析 - Struts2 为调试和配置应用程序提供综合的性能分析,此外,Struts也以嵌入调试工具的形式提供集成调试。
  • 易于修改标签 - 在Struts2 中,可使用Freemarker的模板对标签标记进行调整,而修改标签不需要JSP或是Java知识,基本的HTML、XML和CSS知识就足够了。
  • 促进减少配置 - Struts2 使用各种设置的默认值促进减少配置,而你不需要再配置什么除非是偏离了Struts2 设定的默认设置。
  • 视图技术 - Struts2 为多种视图选项(JSP、Freemarker、Velocity、XSLT等)提供支持。

以上是使Struts2 成为准企业框架的十大优点。

Struts2 框架的缺点

尽管Struts2 有一大列的优点,但我们还是要提到关于它的一些仍需不断改进的缺点:

  • 更大的学习曲线 - 使用Struts MVC,你必须要熟悉JSP、Servlet APIs标准以及一个大型、复杂的框架。
  • 文档缺乏 - 相比于Servlet和JSP APIs标准,Struts的在线资源较少,许多初学者会发现Apache在线文档混乱并缺乏整理。
  • 不够透明 - 相比于使用正常的基于Java的Web应用程序,使用Struts的应用程序有许多是进行在后台,这使得框架不易于理解。

最后说明一点,一个好的框架应该提供各种类型的应用程序都可以使用的通用行为,Struts2 是最好的Web框架之一,并频繁用于RIA(Rich Internet Applications)的发展。

Struts2 入门程序

依赖介绍

在这里插入图片描述

我们需要为每一个struts构建如下组件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4plxwLNe-1629212907857)(struts2.assets/image-20210817090434951.png)]

pom.xml文件依赖

<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
  </dependency>
  <!-- servlet依赖 -->
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
  </dependency>

  <!-- jstl依赖(el表达式) -->
  <dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
  </dependency>

  <dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
  </dependency>

  <!-- mysql依赖 -->
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.29</version>
  </dependency>

  <!-- struts依赖 -->
  <dependency>
    <groupId>org.apache.struts</groupId>
    <artifactId>struts2-core</artifactId>
    <version>2.3.16.3</version>
  </dependency>
  <!--    <dependency>-->
  <!--      <groupId>org.apache.struts</groupId>-->
  <!--      <artifactId>struts2-core</artifactId>-->
  <!--      <version>2.5.14.1</version>-->
  <!--    </dependency>-->


  <dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
  </dependency>
  <dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
  </dependency>

  <dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.8.0</version>
  </dependency>
  <dependency>
    <groupId>commons-lang</groupId>
    <artifactId>commons-lang</artifactId>
    <version>2.6</version>
  </dependency>
  <dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging-api</artifactId>
    <version>1.1</version>
  </dependency>
  <dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.30</version>
  </dependency>
  <dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.23.1-GA</version>
  </dependency>
  <dependency>
    <groupId>org.apache.struts.xwork</groupId>
    <artifactId>xwork-core</artifactId>
    <version>2.3.16.3</version>
  </dependency>
</dependencies>

项目结构如下

在这里插入图片描述

配置过滤器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         id="WebApp_ID" version="3.0">

  <display-name>Struts 2</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <filter>
    <filter-name>struts2</filter-name>
    <filter-class>
      org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
    </filter-class>
  </filter>

  <filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

</web-app>

注意:不同版本的过滤器会有不同

在这里插入图片描述

web.xml配置核心过滤器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         id="WebApp_ID" version="3.0">

  <display-name>Struts 2</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <filter>
    <filter-name>struts2</filter-name>
    <filter-class>
      org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
    </filter-class>
  </filter>

  <filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

</web-app>

创建Action类

package com.qijian.domain;

import com.opensymphony.xwork2.Action;

public class HelloWorldAction implements Action {
    private String name;

    public String execute() throws Exception {
        return "success";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
/*
这是一个非常简单的具有“name”属性的类。对于“name”属性,
我们用标准的getter和setter方法,以及一个返回“success”字符串的执行方法。
Struts2 框架将创建一个“HelloWorldAction”类的对象,
并调用execute方法来响应用户的动作。你把你的业务逻辑放进execute方法里,
最后会返回字符串常量。简单的描述每个URL,你需要实现一个Action类,
你也可以用类名直接作为你的动作名,或者如下面内容所示使用 struts.xml 文件映射到其他name上。
 */

编写配置文件(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.devMode" value="true" />
    <package name="helloworld" extends="struts-default">

        <action name="hello"
                class="com.qijian.domain.HelloWorldAction"
                method="execute">
            <result name="success">/helloworld.jsp</result>
        </action>
    </package>
</struts>

创建试图首页文件(index.jsp)

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
         pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Hello World</title>
</head>
<body>
<h1>Hello World From Struts2</h1>
<form action="hello">
    <label for="name" id="name">Please enter your name</label><br/>
    <input type="text" name="name"/>
    <input type="submit" value="Say Hello"/>
</form>
</body>
</html>

创建返回页面(success.xml)

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>Hello World</title>
</head>
<body>
Hello World, <s:property value="name"/>
</body>
</html>
<%--Taglib指令告知Servlet容器这个页面将使用Struts2 标签,
并且这些标签会被s放在前面。s:property 标签显示Action类“name”属性的值,
这个值是使用HelloWorldAction类的 getName() 方法返回的。--%>

在客户端浏览器上单击 index.jsp 中的超链接时,会发送一个 helloWorld.action 请求,该请求被核心过滤器拦截后,会通过 struts.xml 文件中的配置找到请求对应的 HelloWorldAction,并默认调用 elloWorldAction 中的 execute() 方法返回逻辑视图名,然后再通过配置文件找到并转发给对应的视图页面 success.jsp 中,最后生成响应内容并输出响应的返回结果。

在这里插入图片描述

struts2 执行的完整流程

在这里插入图片描述

从图 1 所示的流程图中可以看出,一个请求在 Struts2 框架中的处理大概可以分为以下几个步骤。

1)首先客户端浏览器发送一个请求(HttpServletRequest)。

2)接着程序会调用 StrutsPrepareAndExecuteFilter,然后询问 ActionMapper 这个请求是否需要调用某个 Action。

3)如果 ActionMapper决定需要调用某个 Action,StrutsPrepareAndExecuteFilter 会把请求的处理交给 ActionProxy

4)ActionProxy通过配置管理器(Configuration Manager)从配置文件(struts.xml)中读取框架的配置信息,从而找到需要调用的 Action 类。

5)ActionProxy 会创建一个ActionInvocation 的实例。

6)ActionInvocation使用命名模式调用 Action,在调用 Action 前,会依次调用所有配置的拦截器(Intercepter1、Intercepter2……)。

7)一旦 Action 执行完,则返回结果字符串,ActionInvocation 就会负责查找结果字符串对应的 Result,然后执行这个 Result。通常情况下 Result 会调用一些模板(JSP 等)呈现页面。

8)产生的 Result 信息返回给ActionInvocation,在此过程中拦截器会被再次执行(顺序与 Action 执行之前相反)。

9)最后产生一个HttpServletResponse 的响应行为,通过StrutsPrepareAndExecuteFilter反馈给客户端。

strust2 配置文件

上面我们会发现和我们平时使用的结构一点不一样,struts2的配置文件不是放在resources下。如果直接放在那里就会读取不了。这时需要配置maven资源过滤配置

  <!--配置Maven 对resource文件 过滤 -->
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <includes>
        <include>**/*.properties</include>
        <include>**/*.xml</include>
        <include>**/*.xls</include>
        <include>**/*.xlsx</include>
      </includes>
      <filtering>true</filtering>
    </resource>
    <resource>
      <directory>src/main/java</directory>
      <includes>
        <include>**/*.properties</include>
        <include>**/*.xml</include>
      </includes>
      <filtering>true</filtering>
    </resource>
  </resources>

配置文件结构如下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
  "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
  "http://struts.apache.org/dtds/struts-2.3.dtd">
<!--文件的根元素-->
<struts>
    <!--<constant>元素用常量的配置-->
    <constant name="struts.enable.DynamicMethodInvocation" value="false" />
    <constant name="struts.devMode" value="true" />
    <!--<package>元素用于包配置,包用于组织 Action 和拦截器等信息,每个包都是由零个或多个拦截器以及 Action 所组成的集合。-->
    <package name="default" namespace="/" extends="struts-default">
        <!--配置Action-->
        <action name="index" class="Xxx"/>
            <!--配置Result-->
            <result type="dispatcher">
                <param name="location">/index.jsp</param>
            </result>
        </action>
    </package>
    <!-- <include>元素用于包含配置 -->
    <include file="example.xml"/>
</struts>

文件配置常用的配置

常量的配置通常使用以下三种方式。

  • struts.xml文件中使用 <constan> 元素配置常量(常用方式)。
  • struts.properties 文件中配置常量。
  • 在 web.xml 文件中通过 <init-param>元素配置常量。

常量配置

​ struts.xml 文件中通过 <constant…/> 元素配置常量时,需要指定两个必填的属性 name 和 value。其中 name 属性用于指定常量的常量名,value 属性用于指定常量的常量值。

<struts>
    <!--设置默认编码集为UTF-8-->
    <constant name="struts.il8n.encoding" value="UTF-8" />
    <!--设置使用开发模式-->
    <constant name="struts.devMode" value="true" />
</struts>

包配置

​ 包用于管理Action 和拦截器,每个包就是多个 Action、多个拦截器、多个拦截器引用的集合。在 struts.xml文件中,使用<package>元素定义包配置,每个 <package> 元素都定义了一个包配置

<package name="default" namespace="/" extends="struts-default">
    ...
</package>
  • name:必填属性,用于指定该包的名称(该名称在配置文件中必须是唯一的),此名称是该包被其他包引用的 Key。
  • namespace:可选属性,用于定义该包的命名空间。
  • extends:可选属性,用于指定该包继承自其他包。其属性值必须是另一个包的 name 属性值,但该属性值通常都设置为 struts-default,这样该包中的 Action 就具有了 Struts2 框架默认的拦截器等功能。

包含配置

​ Struts2只会读取struts.xml文件,一旦通过多个 XML 文件配置 Action,就必须通过 <include>元素包含其他配置文件。

<struts>
    <!--不指定路径,默认在src下时的方式-->
    <include file="struts-post.xml"/>
    <include file="struts-user.xml"/>
    <include file="struts-dept.xml"/>
    <!--配置文件在具体包中时的方式 -->
    <include file="com/mengma/action/struts-product.xml"/>
</struts>

​ file 属性用于指定被包含配置文件的名称。如果被包含的配置文件在 srcresources 路径下,则直接指定文件名即可,如果被包含的配置文件在具体的包中,则需要引入被包含文件的包路径。

Struts2 Action配置

Action 是用于处理请求操作的,它是由StrutsPrepareAndExecuteFilter 分发过来的

Action 是框架的核心类,被称为业务逻辑控制器,主要用于实现对用户请求的处理。

一个Action 类代表一次请求或调用,每个请求的动作都对应一个相应的 Action类。也就是说,用户的每次请求,都会转到一个相应的Action类中,由这个 Action 类进行处理。简而言之,Action 就是用于处理一次用户请求的对象。

实现 Action 控制类通常采用两种方式,分别是实现Action 接口和继承 ActionSupport类。接下来分别对它们进行讲解,具体如下。

实现Action接口

Acton实现了应该实现的规范

Action接口的具体代码

package com.opensymphony.xwork2;
public interface Action {

    public static final String SUCCESS = "success";

    public static final String NONE = "none";

    public static final String ERROR = "error";

    public static final String INPUT = "input";

    public static final String LOGIN = "login";

    public String execute() throws Exception;

}
//execute() 方法是 Action 类的默认请求处理方法,该方法返回一个字符串,而上面五个字符串常量的作用是统一 execute() 方法的返回值。

继承 ActionSupport

package com.qijian.domain;

import com.opensymphony.xwork2.ActionSupport;

 
public class LoginAction extends ActionSupport {
    private static final long serialVersionUID = 1L;
    @Override
    public String execute() throws Exception{
        return super.execute();
    }
}

ActionSupport是 Action 接口的默认实现类,所以继承ActionSupport就相当于实现了 Action 接口。除 Action 接口以外,ActionSupport 类还实现了Validateable、ValidationAware、TextProvider、LocaleProvider 和 Serializable 等接口,这为用户提供了更多的功能。

ActionSupport 类中提供了许多默认方法,这些默认方法包括数据校验的方法、默认的处理用户请求的方法等。如果开发者的 Action 类继承 ActionSupport类,会大大简化 Action 的开发。

需要注意的是,由于自定义的 Action 类继承了ActionSupport 类,因此必须定义一个变量 serialVersionUID

private static final long serialVersionUID = 1L;

配置Action

配置 Action 主要就是配置 struts.xml 文件中 Action 的映射信息。Action 映射是指向一个请求的 URL 映射到一个 Action 类,当一个请求匹配某个 Action 名称时,Struts2 框架就使用这个 Action 确定如何处理请求。

strus.xml文件是通过元素对请求的Action和Action类进行配置的。

<action name="userAction" class="com.mengma.action.UserAction" method="add">
    ...
</action>

在这里插入图片描述

使用通配符

在一个 Action 类中可能有多个业务逻辑处理方法,在配置 Action 时,就需要使用多个 元素。这时就需要使用通配符了。

<package name="user" namespace="/user" extends="struts-default">
    <action name="userAction_*" class="com.mengma.action.UserAction" method="{1}">
        <result>/index.jsp</result>
    </action>
</package>

*在上述代码中,method 属性值中的数字 1 表示匹配第 1 个 。当客户端发送/user/userAction_login.action 这样的请求时, 元素的 name 属性值就被设置成 userAction_login,method属性值就被设置成 login。当客户端发送 /user/userAction_register.action 这样的请求时, 元素的 name 属性值就被设置为 userAction_register,method属性值也被设置成 register。

另外,对 元素也可以采用通配符配置,代码如下所示:

<result>/(1).jsp</result>

当客户端发送userAction_login这样的请求时, 元素被设置成跳转到login.jsp页面。当客户端发送 userAction_register这样的请求时, 元素被设置成跳转到register.jsp页面

Action访问Servlet API

通过 ActionContext 访问

通常开发时需要访问 Servlet API 中的 HttpServletRequest、HttpSessionServletContext 三个接口,它们分别对应 JSP 内置对象 request、session 和 application。

ActionContext是 Action 执行的上下文对象,在 ActionContext中保存了 Action 执行所需要的所有对象,包括 request、session 和 application 等。

在这里插入图片描述

示例

ActionContext context = ActionContext.getContext();
context.put("name","mengma");
context.getApplication().put("name","mengma");
context.getSession().put("name","mengma");

案例

login.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
         pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>登录页面</title>
    <style type="text/css">
        input[type=text],input[type=password]{width:150px}
    </style>
</head>
<body>
<div align="center">
    <form action="login" method="post">
        用户名:<input type="text" name="username"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;码:<input type="password" name="password"/><br/>
        <input type="reset" value="重置"/>
        <input type="submit" value="登录"/>
    </form>
</div>
</body>
</html>

success.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
         pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>登录成功页面</title>
</head>
<body>
<p>${success }<br/></p>
<h2>用户登录信息</h2>
用户名:${username }<br/>
密码:${password }<br/>
</body>
</html>

error

<%@ page language="java" contentType="text/html; charset=utf-8"
         pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>登录失败页面</title>
</head>
<body>
<p>${error }<br/></p>
</body>
</html>

web.xml添加过滤器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>
            org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
        </filter-class>
        </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

创建LoginAction类,继承ActionSupport

package com.qijian.domain;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

public class LoginAction extends ActionSupport {
    private String username; // 用户名
    private String password; // 密码
    // username的getter和setter方法
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    // password的getter和setter方法
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    @Override
    public String execute() throws Exception {
        System.out.println(username);
        System.out.println(password);
        // 获取ActionContext对象
        ActionContext context = ActionContext.getContext();
        if ("admin".equals(username) && "123456".equals(password)) {
            // 将用户名和密码信息放入context对象中
            context.put("username", username);
            context.put("password", password);
            context.put("success", "用户登录成功!");
            return SUCCESS;
        } else {
            // 定义登录失败的错误信息
            context.put("error", "用户名或密码错误,请重新登录!");
            return ERROR;
        }
    }

}

struts.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
        "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <package name="default" extends="struts-default">
        <action name="login" class="com.qijian.domain.LoginAction">
            <result name="success">/success.jsp</result>
            <result name="error">/error.jsp</result>
        </action>
    </package>
</struts>

通过ServletActionContext访问

在这里插入图片描述

上面创建messageAction

package com.qijian.domain;

import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class MessageAction extends ActionSupport{
    public String execute() throws Exception{
        ServletActionContext.getRequest().setAttribute("message","通过ServletActionContext类访问Servlet API");
        return SUCCESS;
    }
}

struts.xml添加配置

<action name="message" class="com.qijian.domain.MessageAction">
    <result name="success">/message.jsp</result>
</action>

创建message.jsp

<div align="center">${requestScope.message }</div>

使用ServletActionContext类也可以在 Action 中访问 Servlet API。虽然如此,该 Action 依然与 Servlet API 直接耦合,这不利于程序的解耦。因此,建议在开发中优先使用 ActionContext,以避免和 Servlet API 耦合。

Action处理请求参数

属性驱动

属性驱动是指在 Action 中通过字段属性进行与页面之间的数据传递,通常使用时会包括两种情况:一种是与基本数据类型的属性对应,另一种是直接使用域对象

  1. 基本数据类型字段驱动方式的数据传递

    在 Struts2 中,可以直接在 Action 中定义各种 Java 基本数据类型的字段,使这些字段与表单数据相对应,并利用这些字段进行数据传递,如下面的代码所示,定义了两个字符串字段 username 和 password,这两个字段分别用于对应页面上的用户名和密码这两个表单域

    public class UserAction extends ActionSupport {
        private String username; // 用户名
        private String password; // 密码
        // 此处省略两个属性的getter和setter方法
        private String execute() throws Exception {
            return SUCCESS;
        }
    }
    
  2. 直接使用域对象字段驱动方式的数据传递

    ​ 在基本数据类型字段驱动方式中,如果传入的数据很多,那么 Action 的属性也会变得很多,再加上属性对应的 getter/setter 方法,势必会导致 Action 非常臃肿。

    ​ 为了解决这一问题,我们可以把属性的 getter/setter 方法从 Action 中提取出来单独作为一个域对象,并在相应的 Action 中直接使用这个域对象。此种方式中的域对象一般以 JavaBean 的形式实现,JavaBean 中所封装的属性要和页面中表单的属性一一对应。此时 JavaBean 将成为数据传递的载体,并可以在其他 Action 中使用。

    user.java

    package com.qijian.domain;
    
    public class User {
        private String username;
        private String password;
    
        // username的getter和setter方法
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        // password的getter和setter方法
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    }
    

    UserLoginAction.java

    package com.qijian.domain;
    
    
    import com.opensymphony.xwork2.ActionContext;
    import com.opensymphony.xwork2.ActionSupport;
    
    public class UserLoginAction extends ActionSupport {
        private User user; // 定义User属性
    
        // user属性的getter和setter方法
    
        public User getUser() {
            return user;
        }
    
        public void setUser(User user) {
            this.user = user;
        }
    
        public String execute() throws Exception {
            // 获取Context对象
            ActionContext context = ActionContext.getContext();
            if ("admin".equals(user.getUsername())
                    && "123456".equals(user.getPassword())) {
                // 将用户名和密码放入session中
                context.getSession().put("username", user.getUsername());
                context.getSession().put("password", user.getPassword());
                return SUCCESS;
            } else {
                context.getSession().put("error", "用户名或密码错误!");
                return ERROR;
            }
    
        }
    }
    

    配置struts

    <action name="userlogin" class="com.qijian.domain.UserLoginAction">
        <result name="success">/loginSuccess.jsp</result>
        <result name="error">/loginError.jsp</result>
    </action>
    

    userLogin.jsp

    <%@ page language="java" contentType="text/html; charset=utf-8"
             pageEncoding="utf-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
        <title>用戶登录页面</title>
        <style type="text/css">
            input[type=text],input[type=password]{width:150px}
        </style>
    </head>
    <body>
    <div align="center">
        <form action="userlogin" method="post">
            用户名:<input type="text" name="user.username"/><br/>
            密&nbsp;&nbsp;&nbsp;&nbsp;码:<input type="password" name="user.password"/><br/>
            <input type="reset" value="重置"/>
            <input type="submit" value="登录"/>
        </form>
    </div>
    </body>
    </html>
    

    loginSuccess.jsp

    <%@ page language="java" contentType="text/html; charset=utf-8"
             pageEncoding="utf-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
        <title>用戶登录成功页面</title>
    </head>
    <body>
    <p align="center">
        您的用戶名是<%=request.getAttribute("user.username") %>
        <br/>
        密码是<%=request.getAttribute("user.password") %>
    </p>
    </body>
    </html>
    

    loginError.jap

    <%@ page language="java" contentType="text/html; charset=utf-8"
             pageEncoding="utf-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
        <title>错误页面</title>
    </head>
    <body>
    <p align="center">
        ${error }<br/>
        <a href="userLogin.jsp">请单击此链接返回登录页面重新登录</a>
    </p>
    </body>
    </html>
    

    ​ 需要注意的是,在使用域对象的属性驱动方式传值时,如果 JSP 页面是负责取值的,那么取值的格式必须为“对象名.属性名”;如果 JSP 页面是负责传值的,那么传值的格式可以为“模型对象名.属性名”。(有的教程说直接属性名也可以但是试了一下发现不行)

模型驱动

​ Action 还有另外一种方式处理请求参数,称为模型驱动(ModelDriven)。

​ 模型驱动方式要求 Action 需要通过实现 ModelDriven 接口接收请求参数,并且要重写 getModel()方法。getModel()方法返回的就是 Action 所使用的数据模型对象。

与属性驱动中直接使用域对象字段驱动方式的数据传递类似,模型驱动方式也是通过 JavaBean 模型进行数据传递的。只要是普通的 JavaBean,就可以充当模型部分,并且 JavaBean 中所封装的属性要与表单的属性一一对应,JavaBean 就是数据传递的载体。

package com.qijian.domain;


import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

public class UserLoginAction extends ActionSupport {
    private User user; // 定义User属性

    // user属性的getter和setter方法

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public String execute() throws Exception {
        // 获取Context对象
        ActionContext context = ActionContext.getContext();
        if ("admin".equals(user.getUsername())
                && "123456".equals(user.getPassword())) {
            // 将用户名和密码放入session中
            context.getSession().put("username", user.getUsername());
            context.getSession().put("password", user.getPassword());
            return SUCCESS;
        } else {
            context.getSession().put("error", "用户名或密码错误!");
            return ERROR;
        }

    }
}

提交表单如下

<%@ page language="java" contentType="text/html; charset=utf-8"
         pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>用戶登录页面</title>
    <style type="text/css">
        input[type=text],input[type=password]{width:150px}
    </style>
</head>
<body>
<div align="center">
<%--    <form action="userlogin" method="post">--%>
<%--        用户名:<input type="text" name="user.username"/><br/>--%>
<%--        密&nbsp;&nbsp;&nbsp;&nbsp;码:<input type="password" name="user.password"/><br/>--%>
<%--        <input type="reset" value="重置"/>--%>
<%--        <input type="submit" value="登录"/>--%>
<%--    </form>--%>
    <form action="loginAction" method="post" name="form1">
        用户名:<input type="text" name="username"/><br/>
        密码:<input type="password" name="password"/><br/>
        <input type="submit" value="登录"/>
    </form>
</div>
</body>
</html>

成功页面

<%@ page language="java" contentType="text/html; charset=utf-8"
         pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>用戶登录成功页面</title>
</head>
<body>
<p align="center">
    您的用戶名是<%=request.getAttribute("username") %>
    <br/>
    密码是<%=request.getAttribute("password") %>
</p>
</body>
</html>

struts配置

        <action name="loginAction" class="com.qijian.domain.LoginAction">
            <result name="success">/loginSuccess.jsp</result>
            <result name="error">/loginError.jsp</result>
        </action>

​ 使用ModelDriver 的方式后,表单中的文本域名称已经不需要添加 user 前缀,页面上的 username 会自动对应到这个 Model 的 username 属性。

与属性驱动相比,模型驱动不需要在 Action 类中定义与表单元素一一对应的所有属性及其各属性的 getter 和 setter 方法,这减少了 Action 类中的代码量。在项目应用中具体使用哪种驱动方法,现给出以下几点建议。

1)要统一整个系统中 Action 的驱动方法,即要么都使用属性驱动,要么都使用模型驱动。

2)如果持久层对象与表单中的属性是一一对应的关系,那么建议使用模型驱动,因为模型驱动方法使 Action 类中的代码更加整洁。

3)如果持久层对象与表单中的属性不是一一对应的关系,那么建议使用属性驱动,因为不是一一对应的关系时,系统中需要提供两个 JavaBean(一个对应表单提交的数据,一个用于持久层对象)。

总之,属性驱动的方法和模型驱动的方法各有优缺点,在实际开发中,需要根据项目实际情况选择使用哪种驱动方式。

Struts2 ResultType结果类型配置

配置Result

	配置`<result>` 元素用于配置 Result 逻辑视图与物理视图之间的映射关系,它有两个可选属性 name 和 type。其中,name 属性用于指定逻辑视图的名称,默认值为 success;type 属性用于指定返回的视图资源的类型,不同的类型代表不同的结果输出,它的默认值是 dispatcher。
<action name="loginAction" class="com.mengma.action.LoginAction">
    <result name="success" type="dispatcher">
        <param name="location">/success.jsp</param>
    </result>
</action>

​ 上述代码为 Action 配置了一个 name 为 success 的 Result 映射,该映射的值可以是 JSP 页面,也可以是一个 Action 的 name 值;这里使用 param 子元素为其指定了 Result 映射对应的物理视图资源为success.jsp

子元素的 name 属性有两个值,分别如下。
  • location:指定该逻辑视图所对应的实际视图资源。
  • parse:指定在逻辑视图资源名称中是否可以使用 OGNL(对象图导航语言)表达式。默认值为 true,表示可以使用,如果设为 false,则表示不支持。

简化:

<action name="loginAction" class="com.mengma.action.LoginAction">
    <result>/success.jsp</result>
</action>

需要注意的是,在 Result 配置中指定实际资源位置时,可以使用绝对路径,也可以使用相对路径。

  • 绝对路径以斜杠“/”开头,例如<result>/success.jsp</result>,相当于当前 Web 应用程序的上下文路径。
  • 相对路径不以斜杠“/”开头,例如<result>success.jsp</result>,相当于当前执行的 Action 路径。

预定义结果类型

​ 当框架调用 Action 对请求进行处理后,就要向用户呈现一个结果视图。在 Struts2 中,预定义了多种 ResultType(结果类型)展示结果视图。

一个结果类型就是实现了 com.opensymphony.xwork2.Result 接口的类,Struts2 把内置的 都放在 struts-default 包中,struts-default 包就是配置包的父包,这个包定义在 struts2-core-x.x.xx.jar 包的根目录下的 struts-default.xml 文件中,在该文件中可以找到相关的<result-type> 的定义,其代码如下所示:

<result-types>
    <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
    <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
    <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
    <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
    <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
    <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
    <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
    <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
    <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>
    <result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" />
    <result-type name="postback" class="org.apache.struts2.dispatcher.PostbackResult" />
</result-types>

​ 在上述代码中,每个 <result-type>元素都是一种视图技术或者跳转方式的封装,其中 name 属性指出在 <result>元素中如何引用这种视图技术或者跳转方式,它对应着 元素的 type 属性。class 属性表示这种结果类型的对应类。

在这里插入图片描述

表 1 列举了 Struts2 中预定义的全部 11 种结果类型,其中 dispatcher 是默认的结果类型,主要用于与 JSP 整合。在这全部 11 种结果类型中,dispatcher 和 redirect 是比较常用的结果类型。

需要注意的是,redirect 与 dispatcher 结果类型非常相似,所不同的是 dispatcher 结果类型是将请求转发到 JSP 视图资源,而 redirect 结果类型是将请求重定向到 JSP 视图资源。如果重定向了请求,那么将丢失所有参数,包括 Action 的处理结果。

dispatcher结果类型

dispatcher 是 Struts2 的默认结果类型,它用于表示转发到指定结果资源。

由于 Struts2 在后台使用 RequestDispatcher 的 forward() 方法转发请求,所以在用户的整个请求/响应过程中,保持的是同一个请求对象,即目标 JSP/Servlet 接收到的请求/响应对象与最初的 JSP/Servlet 的请求/响应对象相同。

dispatcher 结果类型的对应类是 org.apache.struts2.dispatcher.ServletDispatcherResult,该类有 location 和 parse 两个属性,可以通过 struts.xml 配置文件中的 元素的 子元素设置,代码如下所示:

<result name="success" type="dispatcher">
    <param name="location">/success.jsp</param>
    <param name="parse">true</param>
</result>

​ location 参数用于指定 Action 执行完毕后要转向的目标资源;parse 参数是一个布尔类型的值,默认是 true,表示解析 location 参数中的 OGNL 表达式,如果为 false,则不解析。

redirect结果类型

redirect 结果类型用于重定向到指定的结果资源,该资源可以是 JSP 文件,也可以是 Action 类。使用 redirect 结果类型时,系统将调用 HttpServletResponse 的 sendRedirect() 方法将请求重定向到指定的 URL。

redirect 结果类型的对应类是 org.apache.struts2.dispatcher.ServletRedirectResult。在使用 redirect 时,用户要完成一次和服务器之间的交互,浏览器需要发送两次请求,请求过程如图 1 所示

在这里插入图片描述

使用 redirect 结果类型的工作过程如下。

1)浏览器发出一个请求,Struts2框架调用对应的Action实例对请求进行处理。

2)Action返回success结果字符串,Struts2框架根据这个结果选择对应的结果类型,这里使用的是redirect结果类型。

3)ServletRedirectResult在内部使用HttpServletResponse的sendRedirect()方法将请求重新定向到目标资源。

4)浏览器重新发起一个针对目标资源的新请求。

5)目标资源作为响应呈现给用户。

例如可对上面的例子进行配置

<action name="login" class="com.mengma.action.LoginAction">
    <result name="success" type="redirect">/success.jsp</result>
    <result name="error" type="dispatcher">/error.jsp</result>
</action>

​ 将成功登录的结果类型设置为 redirect,它表示当 Action 处理请求后会重新生成一个请求。将错误的结果类型设置为 dispatcher,这也是结果类型的默认值。

注意:

  1. redirect是利用的重定向,由于使用 redirect 重定向到其他资源时,将重新产生一个请求,并且原来的请求内容和参数将全部丢失,所以页面中的用户名和密码没有显示。
  2. 当用户名和密码错误时,使用的是 dispatcher 结果类型,此时页面将跳转到 error.jsp,由于是请求转发行为,所以地址栏中显示的还是 login 的请求信息,但页面中显示的是 error.jsp 中的内容

拦截器简介

在 Struts2 框架中,拦截器是其重要的组成部分,Struts2 的很多功能(数据校验、对象类型转换、文件上传等)都是构建在拦截器之上的。

当多个拦截器组合在一起时就形成了拦截器链(Interceptor Chain)或拦截器栈(Interceptor Stack)。

拦截器链就是指对应各个功能的拦截器按照一定的顺序排列在一起形成的链,而拦截器链组成的集合就是拦截器栈。当有适配连接器栈的访问请求进来时,这些拦截器就会按照之前定义的顺序被调用。

在通常情况下,拦截器都是以代理方式调用的,它在一个 Action 执行前后进行拦截,围绕着 Action 和 Result 的执行而执行,其工作方式如图 1 所示。

从图 1 中可以看出,Struts2 拦截器的实现原理与 Servlet 过滤器的实现原理类似,它以链式执行,对真正要执行的方法(execute())进行拦截。

在这里插入图片描述

在执行 Action 的 execute() 方法之前会执行一次拦截,在 Action 和 Result 执行之后,拦截器会再次执行(与先前的调用顺序相反)。在此链式执行的过程中,每一个拦截器都可以直接返回,从而终止余下的拦截器、Action 及 Result 的执行。

struts2拦截器的配置与使用

拦截器

要使用拦截器,首先要对它进行配置。拦截器的配置是在 struts.xml 文件中完成的,它通常以 标签开头,以 标签结束。定义拦截器的语法格式如下所示:

<interceptor name="interceptorName" class="interceptorClass">
    <param name="paramName">paramValue</param>
</interceptor>

​ 元素的 name 属性用于指定拦截器的名称,class 属性用于指定拦截器的实现类。有时,在定义拦截器时需要传入参数,这时需要使用 标签,其中 name 属性用于指定参数的名称,paramValue 表示参数的值

拦截器栈

​ 在实际的项目开发中,经常需要在 Action 执行之前执行多个拦截动作,如登录日志记录、权限管理等。

为了方便代码管理和程序的执行,开发者通常会将这些拦截器组成一个拦截器栈,在使用时,可以将栈内的多个拦截器当成一个整体引用。当拦截器栈被附加到一个 Action 上时,在执行 Action 之前必须先执行拦截器栈中的每一个拦截器。

定义拦截器栈使用 元素和 子元素,当配置多个拦截器时,需要使用 元素指定多个拦截器,配置语法如下所示:

<interceptors>
    <interceptor-stack name="interceptorStackName">
        <interceptor-ref name="interceptorName"/>
        ...
    </interceptor-stack>
</interceptors>

interceptorStackName 值表示配置的拦截器栈的名称;interceptorName 值表示拦截器的名称。除此之外,在一个拦截器栈中还可以包含另一个拦截器栈,示例代码如下所示:

<package name="default" namespace="/" extends="struts-default">
    <!--声明拦截器-->
    <interceptors>
        <interceptor name="interceptor1" class="interceptorClass"/>
        <interceptor name="interceptor2" class="interceptorClass"/>
        <!--定义一个拦截器栈myStack,该拦截器栈中包含两个拦截器和一个拦截器栈-->
        <interceptor-stack name="myStack">
            <interceptor-ref name="defaultStack"/>
            <interceptor-ref name="interceptor1"/>
            <interceptor-ref name="interceptor2"/>
        </interceptor-stack>
    </interceptors>
</package>

​ 定义的拦截器栈的名称是myStack,在myStack栈中,除了引用了两个自定义的拦截器 interceptor1 和 interceptor2 以外,还引用了一个内置拦截器栈defaultStack,这个拦截器是必须要引入的。

默认拦截器

​ 如果想对一个包下的 Action 使用相同的拦截器,则需要为该包中的每个 Action 都重复指定同一个拦截器,这样写显然过于繁琐。为了解决此问题,Struts2 中支持使用默认拦截器,它可以对其指定的包中的所有 Action 都起到拦截作用。

一旦为某一个包指定了默认拦截器,并且该包中的 Action 未显示指定拦截器,则会使用默认拦截器。反之,若此包中的 Action 显示的指定了某个拦截器,则该默认拦截器将会被屏蔽。此时,如果还想使用默认拦截器,则需要用户手动配置该默认拦截器的引用。

配置默认拦截器需要使用 元素,此元素为 元素的子元素。其语法格式如下所示:

<default-interceptor-ref name="拦截器(栈)的名称"/>

​ name 属性的值必须是已经存在的拦截器或拦截器栈的名称。下面用该语法格式配置一个默认拦截器,示例代码如下

<package name="default" namespace="/" extends="struts-default">
    <!--声明拦截器-->
    <interceptors>
        <interceptor name="interceptor1" class="interceptorClass"/>
        <interceptor name="interceptor2" class="interceptorClass"/>
        <!--定义一个拦截器栈myStack,该拦截器栈中包含两个拦截器和一个拦截器栈-->
        <interceptor-stack name="myStack">
            <interceptor-ref name="defaultStack"/>
            <interceptor-ref name="interceptor1"/>
            <interceptor-ref name="interceptor2"/>
        </interceptor-stack>
    </interceptors>
    <!--配置包下的默认拦截器,既可以是拦截器,也可以是拦截器栈-->
    <default-interceptor-ref name="myStack"/>
    <action name="login" class="com.mengma.action.LoginAction">
        <result name="input">/login.jsp</result>
    </action>
</package>

​ 指定了包下面的默认拦截器为一个拦截器栈,该拦截器栈将会作用于包下所有的 Action。

注意:每一个包下只能定义一个默认拦截器,如果需要多个拦截器作为默认拦截器,则可以将这些拦截器定义为一个拦截器栈,再将这个拦截器栈作为默认拦截器即可。

内键拦截器

点击浏览

自定义拦截器

1 , 实现接口

通常开发人员所编写的自定义拦截器类都会直接或间接地实现 com.opensymphony.xwork2.interceptor.Interceptor 接口。Interceptor接口中的主要代码如下

public interface Interceptor extends Serializable{
    void init();
    void destroy();
    String intercept(ActionInvocation invocation) throws Exception;
}

接口共提供了以下三个方法。

1)void init()

该方法在拦截器被创建后会立即被调用,它在拦截器的生命周期内只被调用一次。可以在该方法中对相关资源进行必要的初始化。

2)void destroy()

该方法与 init() 方法相对应,在拦截器实例被销毁之前,将调用该方法释放和拦截器相关的资源,它在拦截器的生命周期内,也只被调用一次。

3)String intercept(ActionInvocation invocation)throws Exception

该方法是拦截器的核心方法,用于添加真正执行拦截工作的代码,实现具体的拦截操作,它返回一个字符串作为逻辑视图,系统根据返回的字符串跳转到对应的视图资源。每拦截一个动作请求,该方法就会被调用一次。

该方法的 ActionInvocation 参数包含了被拦截的 Action 的引用,可以通过该参数的 invoke() 方法,将控制权转给下一个拦截器或者转给 Action 的 execute() 方法。

2,继承AbstractIntercepter

​ 在实际开发过程中,更常用的一种方式是继承抽象拦截器类 AbstractIntercepter。

AbstractIntercepter 类实现了 Interceptor 接口,并且提供了 init() 方法和 destroy() 方法的空实现。使用时,可以直接继承该抽象类,而不用实现那些不必要的方法。AbstractInterceptor 类中定义的方法如下

public abstract class AbstractInterceptor implements Interceptor{
    public void init(){}
    public void destroy(){}
    public abstract String intercept (ActionInvocation invocation) throws Exception;
}

​ AbstractInterceptor 类已经实现了 Interceptor 接口的所有方法,一般情况下,只需继承 AbstractInterceptor 类,实现 interceptor() 方法就可以创建自定义拦截器。

需要注意的是,只有当自定义的拦截器需要打开系统资源时,才需要覆盖 AbstractInterceptor 类的 init() 方法和 destroy() 方法。与实现 Interceptor 接口相比,继承 AbstractInterceptor 类的方法更为简单。

自定义拦截器权限控制案例

项目结构

在这里插入图片描述

web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <!-- 定义filter -->
  <filter>
    <!-- filter名字 -->
    <filter-name>struts2</filter-name>
    <!-- filter的实现类,此处是Struts2的核心过滤器 -->
    <filter-class>
      org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
    </filter-class>
  </filter>
  <filter-mapping>
    <!-- filter的名字,必须是filter元素中已经声明过的过滤器的名字 -->
    <filter-name>struts2</filter-name>
    <!-- 定义filter负责拦截的URL地址 -->
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <!-- 首页 -->
  <welcome-file-list>
    <welcome-file>main.jsp</welcome-file>
  </welcome-file-list>
</web-app>

the code of user.java

package com.mengma.domain;


public class User {
    private String username; // 用户名
    private String password; // 密码

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

创建LoginAction

package com.mengma.action;

import com.mengma.domain.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

public class LoginAction extends ActionSupport implements ModelDriven<User> {
    private static final long serialVersionUID = -8493698886438630994L;
    private User user = new User();
    @Override
    public User getModel() {
        return user;
    }
    public String execute() throws Exception {
        // 获取ActionContext
        ActionContext actionContext = ActionContext.getContext();
        if ("admin".equals(user.getUsername())
                && "123456".equals(user.getPassword())) {
            // 将用户存储在session中
            actionContext.getSession().put("user", user);
            return SUCCESS;
        } else {
            actionContext.put("msg", "用户名或密码错误,请重新登录!");
            return INPUT;
        }
    }
}

BookAction 类

package com.mengma.action;

import com.opensymphony.xwork2.ActionSupport;


public class BookAction extends ActionSupport {
    private static final long serialVersionUID = 5640989517690867879L;
    // 购买图书
    public String buy() {
        return SUCCESS;
    }
}

自定义拦截器的使用过程

  1. 用户自定义的拦截器类,必须实现 Interceptor 接口或继承 AbstractInterceptor 类。
  2. 需要在 Struts.xml 中定义自定义的拦截器。
  3. 在 Struts.xml 中的 Action 中使用拦截器。

PrivilegeInterceptor.java

package com.mengma.interceptor;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class PrivilegeInterceptor extends AbstractInterceptor {
    private static final long serialVersionUID = 193664972753450682L;
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        // 得到 ActionContext
        ActionContext actionContext = invocation.getInvocationContext();
        // 获取User对象
        Object user = actionContext.getSession().get("user");
        if (user != null) {
            return invocation.invoke(); // 继续向下执行
        } else {
            actionContext.put("msg", "您还未登录,请先登录!");
            return Action.LOGIN; // 如果用户不存在,则返回login值
        }
    }
}

struts.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
        "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <package name="struts2" namespace="/" extends="struts-default">
        <!-- 声明拦截器 -->
        <interceptors>
            <interceptor name="privilege"
                         class="com.mengma.interceptor.PrivilegeInterceptor" />
            <interceptor-stack name="myStack">
                <interceptor-ref name="defaultStack" />
                <interceptor-ref name="privilege" />
            </interceptor-stack>
        </interceptors>
        <!-- 用户登录操作 -->
        <action name="login" class="com.mengma.action.LoginAction">
            <result>/main.jsp</result>
            <result name="input">/login.jsp</result>
        </action>
        <!-- 关于book操作 -->
        <action name="book_*" class="com.mengma.action.BookAction"
                method="{1}">
            <result>/success.jsp</result>
            <result name="login">/login.jsp</result>
            <!-- 在action中使用自定义拦截器 -->
            <interceptor-ref name="myStack" />
        </action>
    </package>
</struts>

主页 main.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
         pageEncoding="utf-8"%>
<!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>主页</title>
</head>
<body>
<table border="0">
    <tr>
        <td>《SSH框架整合实战教程》</td>
        <td><a href="/struts2demo03/book_buy">购买</a></td>
    </tr>
</table>
</body>
</html>

登录页面 login.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
         pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>登录</title>
    <style type="text/css">
        input[type=text],input[type=password]{width:150px}
    </style>
</head>
<body>
<div align="center">
    <form action="/struts2demo03/login.action" method="post">
        <table>
            <tr>
                <td><label style="text-align:right;">用戶名:</label></td>
                <td><input type="text" name="username"></td>
                <td><span style="color:#F00">${requestScope.msg }</span></td>
            </tr>
            <tr>
                <td><label style="text-align:right;">密&nbsp;&nbsp;&nbsp;&nbsp;码:</label></td>
                <td><input type="password" name="password"></td>
            </tr>
            <tr>
                <td align="right" colspan="2"><input type="submit" value="登录"></td>
            </tr>
        </table>
    </form>
</div>
</body>
</html>

操作成功页面 success.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
         pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>成功页面</title>
</head>
<body>
尊敬的会员${user.username },您已成功购买商品,祝您购物愉快!
</body>
</html>

运行截图

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

Struts2标签库的分类

在这里插入图片描述

<s:if>、<s:elseif>、<s:else>标签

语法

<s:if test="表达式1">
    标签体
</s:if>
<s:elseif test="表达式2">
    标签体
</s:elseif>
<s:else>
    标签体
</s:else>

<s:iterator>标签

<s:iterator> 标签主要用于对集合中的数据进行迭代,它可以根据条件遍历集合中的数据。<s:iterator> 标签的属性及相关

在这里插入图片描述

如果在 <s:iterator> 标签中指定 status 属性,就可以通过该属性获取迭代过程中的状态信息,如元素数、当前索引值等。通过 status 属性获取信息的方法如表 2 所示(假设其属性值为 st)

在这里插入图片描述

案例:

<%@ page language="java" contentType="text/html; charset=utf-8"
         pageEncoding="utf-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!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>控制标签</title>
</head>
<body>
<center>
    <table border="1px" cellpadding="1">
        <s:iterator var="name" value="{'apple','orange','pear','banana'}" status="st">
            <s:if test="#st.odd">
                <tr style="background-color:white;">
                    <td><s:property value="name"/></td>
                </tr>
            </s:if>
            <s:else>
                <tr style="background-color:grey;">
                    <td><s:property value="name"/></td>
                </tr>
            </s:else>
        </s:iterator>
    </table>
</center>
</body>
</html>

引入了 Struts2 的标签库,然后在

标签内,使用 <s:iterator> 标签循环输出集合中的,并将通过该标签 status 属性的 odd 方法获取的值作为 <s:if> 和 <s:else> 标签的判断条件,对表格进行控制显示。

status属性:可选属性,该属性在迭代时会产生一个IteratorStatus对象,该对象可以判断当前元素的位置,包含了以下属性方法

int getCount(); 迭代元素个数

int getIndex(); 迭代元素当前索引

boolean getFirst(); 是否为第一个

boolean getEven(); 是否为偶

boolean getLast(); 是否最后一个

bolean getOdd();? ? 是否为奇

了解更多点击

​ 表格的奇数行变为白色,偶数行变为灰色。这是因为在 controlTags.jsp 中使用遍历集合时,通过标签判断其所在索引的奇偶从而改变表格的颜色。

在这里插入图片描述

<s:property>标签

<s:property> 标签的作用是输出指定的值,通常输出的是 value 属性指定的值,<s:property> 标签的属性及属性说明如下。

  • value:可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出 ValueStack 栈顶的值(关于值栈内容会在后面教程中进行讲解)。
  • id:可选属性,指定该元素的标识。
  • default:可选属性,如果要输出的属性值为 null,则显示 default属性的指定值。
  • escape:可选属性,指定是否忽略 HTML 代码。默认值是 true,即忽略输出值中的 HTML 代码。

案例:

<%@ page language="java" contentType="text/html; charset=utf-8"
         pageEncoding="utf-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!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>property标签</title>
</head>
<body>
输出字符串:
<s:property value="'this is a string'"/><br/>
输出默认值:
<s:property value="" default="default_value"/><br/>
忽略HTML代码:
<s:property value="'<h2>www.w3school.com.cn</h2>'" escape="true"/><br/>
不忽略HTML代码:
<s:property value="'<h2>www.w3school.com.cn</h2>'" escape="false"/><br/>
</body>
</html>

在这里插入图片描述

<s:a>标签

<s:a> 标签与 HTML 中的 标签类似,主要用于构造 HTML 页面中的超链接

在这里插入图片描述

格式:

<s:a href="链接地址"></s:a>
<s:a namespace="" action="">www.baidu.com</s:a>

<s:debug>标签

<s:debug> 标签用于输出服务端对象(如 request、application、ValueStack 等)中的信息

<s:debug> 标签只有一个 id 属性,表示 <s:debug> 标签的一个引用,通常不使用该属性。在使用 <s:debug> 标签后,网页中会生成一个 Debug 的链接,单击该链接,网页中将输出各种服务器对象的信息

<s:include>标签

​ <s:include> 标签用于在当前页面中包含另一个 Web 资源(如 HTML、JSPServlet 等)。该标签有两个属性 id 和 value。其中 id 是可选属性,表示该标签的引用;value 是必填属性,用于指定被包含的 Web 资源文件。

​ 在 <s:include> 标签中还可以指定多个 <s:param/> 子标签给被包含的 Web 资源传递请求参数。下面通过案例演示 <s:include> 标签的使用。

被包含页面( file.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!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>property标签</title>
</head>
<body>
    <h2>这是被包含页面includefile.jsp</h2><br/>
    传递的参数为:<%out.print(request.getParameter("username")); %>
</body>
</html>

包含页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<!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>includeTags</title>
</head>
<body>
    <h3>这是包含页面includeTags.jsp</h3><br/>
    <s:include value="file.jsp">
        <s:param name="username" value="'小韩'"/>
    </s:include>
</body>
</html>

在这里插入图片描述

<s:param>

<s:param> 标签主要用于为其他标签提供参数,通常要与其他标签一起使用。在上一部分使用 <s:include> 标签时,就使用了 <s:param> 标签给被包含的页面传递参数。<s:param> 标签有两种用法,具体如下。

一种用法是通过标签体指定参数值,用法如下所示:

<s:param name="color">red</s:param>

另一种用法是使用 value 属性指定参数值,用法如下所示:

<s:param name="color" value="'red'"/>

上述两种用法的功能一样,不同点在于使用 value 属性设置参数值时,需要添加单引号,而使用标签体设置参数值时,不需要添加单引号。

注意:在使用 value 属性指定参数时,如果不添加单引号,则表示该值为一个引用对象,如果该对象不存在,则为其属性赋值为 null。

主题

在这里插入图片描述

表单标签

非表单标签

说明,本人有写笔记的习惯,所以一边学习的痛时也,对教程修改,来做到适合自己的笔记。
本次学习来源于:http://c.biancheng.net/struts2/,感谢。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值