Struts2入门

1.概述

1.1定义

 Struts2是Apache 公司推出的一个基于 MVC的轻量级 Web 框架。

1.2由来

Struts 框架目前有两个版本:Struts1.x 和 Struts2.x。Struts1 是最早的基于 MVC 模式的轻量级 Web 框架。Struts2 是在 Struts1 和 WebWork 技术的基础上进行合并后的全新框架。虽然 Struts2 的名字与 Struts1 相似,但其设计思想有很大不同,因为 Struts2 是以 WebWork 为核心的,它是 WebWork 技术与 Struts1 技术的结合,所以 Struts2 可以理解为 WebWork 的更新产品。

2.入门案例

源码:https://github.com/zhongyushi-git/hibernate-collection.git。下载代码后,示例代码在struts2-maven文件夹下

1)新建一个maven的web项目

2)导入依赖

<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>7.3.1</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
<version>7.3.1</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-tree</artifactId>
<version>7.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.3.37</version>
</dependency>
<dependency>
<groupId>org.apache.struts.xwork</groupId>
<artifactId>xwork-core</artifactId>
<version>2.3.37</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.24.0-GA</version>
</dependency>
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.0.21</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.18</version>
</dependency>
</dependencies>

3)在web.xml配置核心过滤器

    <!-- 配置Struts2核心过滤器 -->
    <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>

4)新建HelloWorldController,继承ActionSupport类

package com.zxh.controller;

import com.opensymphony.xwork2.ActionSupport;

public class HelloWorldController extends ActionSupport {

    public String execute() {
        return SUCCESS;
    }
}

5)在资源目录下新建struts.xml,进行配置

<?xml version="1.0" encoding="UTF-8"?>
<!-- 指定 Struts2 配置文件的 DTD 信息 -->
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
        "http://struts.apache.org/dtds/struts-2.3.dtd">
<!-- Struts2配置文件的根元素 -->
<struts>
    <!-- Struts2的Action必须放在指定的包空间下定义 -->
    <package name="hello" namespace="/" extends="struts-default">
        <!-- 定义 action,该 action 对应的类为 com.zxh.controller.HelloWorldController 类-->
        <action name="helloWorld" class="com.zxh.controller.HelloWorldController">
            <!-- 定义处理结果和视图资源之间的映射关系 -->
            <result name="success">/success.jsp</result>
        </action>
    </package>
</struts>

6)在webapp下新建index.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>
<a href="/helloWorld.action">
    第一个 Struts2 程序!
</a>
</body>
</html>

7)在webapp下新建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>
    欢迎来到Struts2的世界!zxh
</body>
</html>

8)配置tomcat,设置访问路径为/,启动项目。访问localhost:8080,显示下图的页面

9)点击连接,会进入成功的页面

 自此,Struts2的第一个入门案例已经结束了。

10)流程分析 

在启动项目时,会加载struts.xml文件;在首页点击链接时,会给发送helloWorld.action的请求,该请求会被核心过滤器进行拦截,然后根据struts.xm配置找到对应的HelloWorldAction并自动调用 HelloWorldAction 中的 execute() 方法返回逻辑视图名,然后再通过配置文件找到并转发给对应的视图页面 success.jsp 中,最后生成响应内容并输出响应的返回结果。

3.struts2执行过程

1)首先客户端浏览器发送一个请求(HttpServletRequest),程序会调用 StrutsPrepareAndExecuteFilter,然后询问 ActionMapper 这个请求是否需要调用某个 Action。

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

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

4)ActionProxy 会创建一个 ActionInvocation 的实例。使用命名模式调用 Action,在调用 Action 前,会依次调用所有配置的拦截器(Intercepter1、Intercepter2……)。

5)一旦 Action 执行完,则返回结果字符串,ActionInvocation 就会负责查找结果字符串对应的 Result,然后执行这个 Result。

6)产生的 Result 信息返回给 ActionInvocation,在此过程中拦截器会被再次执行

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

4.struts.xml分析

struts.xml 是 Struts2 框架的核心配置文件,主要用于配置 Action 和请求的对应关系,以及配置逻辑视图和物理视图资源的对应关系。在文件中<struts> 是文件的根元素,其他配置都在此标签内部:

<struts>
    
    ......
   
</struts>

4.1常量配置

对于不需要经常变化的配置都可以放到常量配置中。常量使用<constant> 标签进行配置,要指定两个属性 name 和 value。其中 name 属性用于指定常量的常量名,value 属性用于指定常量的常量值。

下面的配置就配置了使用的编码及使用开发模式:

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

4.2包配置

包用于管理 Action 和拦截器,在这个包里配置多个拦截器和Action。下面是入门案例的配置:

<!-- Struts2的Action必须放在指定的包空间下定义 -->
<package name="hello" namespace="/" extends="struts-default">
<!-- 定义 action,该 action 对应的类为 com.zxh.controller.HelloWorldController 类-->
<action name="helloWorld" class="com.zxh.controller.HelloWorldController">
<!-- 定义处理结果和视图资源之间的映射关系 -->
<result name="success">/success.jsp</result>
</action>
</package>

1)name:(必)定义包名,名字是唯一的,不能重复。

2)namespace:定义该包的命名空间

3)extends:指定该包继承自其他包。其属性值必须是另一个包的 name 属性值,但该属性值通常都设置为 struts-default,这样该包中的 Action 就具有了 Struts2 框架默认的拦截器等功能。

<action>标签在后续章节说明。

4.3包含配置

Struts2 允许将一个配置文件分解成多个配置文件,在 struts.xml 中使用<include> 标签引入其他包含的配置文件。 file 属性用于指定被包含配置文件的名称。如果被包含的配置文件在 src 路径下,则直接指定文件名即可,如果被包含的配置文件在具体的包中,则需要引入被包含文件的包路径。

<include file="struts-post.xml"/>
<!--配置文件在具体包中时的方式-->
<include file="com/zxh/action/struts-product.xml"/>

4.4 action配置

struts2通过Action类来完成请求或调用,每个请求的动作都对应一个相应的 Action 类。因此需要对Action 控制类进行实现 ,方式分别是 实现Action 接口和继承 ActionSupport 类。

4.4.1实现Action 接口

新建一个action类HelloWorldController2,实现Action接口,重写execute方法,返回Success:

package com.zxh.controller;

import com.opensymphony.xwork2.Action;

public class HelloWorldController2  implements Action {

    @Override
    public String execute() throws Exception {
        return SUCCESS;
    }
}

把struts.xml的action的class属性改为HelloWorldController2所在的类,项目重启后点击链接同样可以跳转到success.jsp页面。

打开Action类,可以看到,就定义了几个常量和一个方法,如下图

 

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

4.4.2继承 ActionSupport 类

在入门案例中就采用的是这种方式,重写了execute方法。ActionSupport是Action接口的默认实现类,那么继承 ActionSupport 就相当于实现了 Action 接口。

为了代码的严谨性,下面的一行代码必须出现在action类中:

private static final long serialVersionUID = 1L;

4.4.3配置映射

既然一个action对应一个请求,那么多个请求url就需要配置多个action,进行一一映射。使用<action>标签进行配置,此标签需要放在<package>标签里面

<action name="helloWorld" class="com.zxh.controller.HelloWorldController2" method="add">
<!-- 定义处理结果和视图资源之间的映射关系 -->
<result name="success">/success.jsp</result>
</action>

它有三个常用的属性,name指定了 Action 所处理请求的 URL,该名称必须唯一。class指定 Action 的实现类。method指定请求 Action 时调用的方法。如果指定了 method 属性,则该 Action 会调用 method 属性中指定的方法,如果不指定 method 属性,则 Action 会调用 execute() 方法。

4.4.4action通配符

正如4.4.3所说,一个action对应一个请求,那么对于相同前缀的请求,若全部配置action,则工作量很大,这时可以使用通配符(*)进行配置,需要指定method,使用{通配符的位置}来使用符合条件的通配符。

现有几个请求:userAction_add.action用户添加,userAction_list.action用户列表查询,userAction_update.action用户修改,userAction_delete.action用户删除。

1)struts.xml配置action

<action name="userAction_*" class="com.zxh.controller.UserAction" method="{1}">
<!-- 定义处理结果和视图资源之间的映射关系 -->
<result name="success">/{1}.jsp</result>
</action>

使用了一个" * ",也就是一个通配符,在method和result中都使用了{1},数字1表示匹配第 1 个 *。若使用多个通配符,不同的数字就指定匹配不同位置的通配符。下面就是使用两个通配符的示例代码:

<action name="*_*" class="com.zxh.user.{1}Action" method="{2}">
   <result name="success">/{1}_{2}.jsp</result>
</action>

2)新建UserAction类

package com.zxh.controller;

import com.opensymphony.xwork2.ActionSupport;

public class UserAction extends ActionSupport {

public String add(){
return SUCCESS;
}

public String list(){
return SUCCESS;
}

public String update(){
return SUCCESS;
}

public String delete(){
return SUCCESS;
}
}

3)在index.jsp添加4个超链接,指定请求并在webapp下新建4个jsp页面(见源码)

<a href="userAction_add.action">添加用户</a><br>
<a href="userAction_list.action">查询用户</a><br>
<a href="userAction_update.action">修改用户</a><br>
<a href="userAction_delete.action">删除用户</a><br>

4)测试。点击不同的超链接可以跳转到不同的页面。也就说,当请求是userAction_add.action时,那么action的name值就是userAction_add,method的值是add,<result>的内容是add.jsp。通过设置这样的通配符,就实现了不同的请求访问不同的页面,简化了xml的配置。

4.5 result配置

<result> 标签用于配置 Result 逻辑视图与物理视图之间的映射关系。

4.5.1 属性说明

先看完整的代码:

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

属性说明:

A. (可选)name 属性指定逻辑视图的名称,默认值为 success。

B. (可选)type 属性指定返回的视图资源的类型,不同的类型代表不同的结果输出,默认值是 dispatcher;还有另一个值是redirect。

C. (可选)<param>指定映射对应的物理视图资源。其name属性有另一个值parse(是否可以使用 OGNL表达式,默认true)

既然上述的属性都是可选的,那么其内容可以简写如下,不过一般会指定name属性,方便区分:

<result>/success.jsp</result>

对应result的name属性,值是dispatcher表示请求转发,通常用于JSP页面,在请求前后可以传递数据;值是redirect表示重定向,页面间无法传递信息。关于这两种方式的配置在下一章节进行配置说明。

5.action访问Servlet API

5.1前言

在servlet中,可以使用HttpServletRequest、HttpSession 和 ServletContext 三个接口,但在struts2中,已经和servlet进行了解耦,要想获得Servelet API,需要使用ActionContext或ServletActionContext接口。

5.2使用ActionContext

ActionContext 是 Action 执行的上下文对象,在 ActionContext 中保存了 Action 执行所需要的所有对象,包括 request、session 和 application 等。那么其设置值的方式如下:

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

上述分别给 request、application 和 session 中设置了name的值,不同的情景使用不同的值,设置后在jsp页面直接通过EL表达式获取即可。下面以request进行说明:

1)新建登录的action

package com.zxh.controller;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
public class LoginAction extends ActionSupport {

    private String username; // 用户名
    private String password; // 密码

    public String login(){
        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;
        }
    }
}

在这个action类中,对于成功和失败返回了不同的字符串,对应不同的页面。把提示信息放在context对象中。

2)在struts.xml配置action

<action name="login" class="com.zxh.controller.LoginAction" method="login">
<result name="success">/loginSuccess.jsp</result>
<result name="error">/loginError.jsp</result>
</action>

3)在webapp下新建登录页面login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>用户登录</title>
</head>
<body>
<div align="center">
    <form action="login.action" method="post">
        用户名:<input type="text" name="username"/><br/>
        密码:<input type="password" name="password"/><br/>
        <input type="reset" value="重置"/>
        <input type="submit" value="登录"/>
    </form>
</div>
</body>
</html>

对应请求,也可以省略后缀.laction,直接是login.

4)新建登录成功的页面loginSuccess.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false"%>
<html>
<head>
    <title>登录成功</title>
</head>
<body>
    <p>${success }<br/></p>
    <h2>用户登录信息</h2>
    用户名:${username }<br/>
    密码:${password }<br/>
</body>
</html>

需要注意的是,当使用idea创建的jsp文件在页面原样输出EL表达式时,需要设置jsp的头信息

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>

5)新建登录成功的页面loginError.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>登录失败</title>
</head>
<body>
<p>${error }<br/></p>
</body>
</html>

6)启动项目,访问localhost:8080/login.jsp,输入错误的用户名或密码会跳转到错误页面显示错误信息,如下图

输入正确的用户名和密码(admin,123456)则跳转到成功的页面显示登录信息,地址栏显示的是login。如下图

7)对struts.xml的<result>进行修改,测试其type属性(后面的代码还是以上述的配置为主)

<action name="login" class="com.zxh.controller.LoginAction" method="login">
<result name="success" type="redirect">/loginSuccess.jsp</result>
<result name="error" type="dispatcher">/loginError.jsp</result>
</action>

这里把登录成功的页面设置为重定向,错误的页面还是采用默认的请求转发。

8)重启项目,访问localhost:8080/login.jsp,输入错误的用户名或密码会跳转到错误页面显示错误信息(同上错误页面)。输入正确的用户名和密码(admin,123456)则跳转到成功的页面,但没有显示登录的信息,原因是数据丢失了。地址栏显示的是loginSuccess.jsp,如下图:

5.3使用ServletActionContext

 通过ActionContext的讲解,用法就很简单了,对于ServletActionContext就只简单说明。

在action中设置内容

ServletActionContext.getRequest().setAttribute("name","lisi");

在jsp页面获取值

${requestScope.name}

6.Action请求参数的处理

6.1属性驱动

指在 Action 中通过字段属性进行与页面之间的数据传递。

6.1.1基本数据类型字段驱动

java的基本数据类型的字段,保持其名称与表单数据相对应,提供get和set方法,就可以在action中进行数据的传递。下面的代码是使用ActionContext时的部分代码:

@Setter
@Getter
public class LoginAction extends ActionSupport {

    private String username; // 用户名
    private String password; // 密码

    
   ......  
}

这种方式适用于表单数据的交互。

6.1.2域对象字段驱动

对于基本数据类型的字段,当有多个字段需要传递时,就需要在action写很多的属性。

为了代码的简洁,可以把这些属性封装为一个对象,在action中使用这个对象即可。这种方式就是域对象字段驱动。

1)新建一个User类,存放传递的属性

package com.zxh.entity;

import lombok.Data;

@Data
public class User {

    private String username; // 用户名
    private String password; // 密码
}

2)在LoginAction类中引入并使用。此时需要给user属性提供get和set方法。

private User user;


public String login(){
ActionContext context=ActionContext.getContext();
System.out.println("user:"+user);
if ("admin".equals(user.getUsername()) && "123456".equals(user.getPassword())) {
// 将用户名和密码信息放入context对象中
context.put("username", user.getUsername());
context.put("password", user.getPassword());
context.put("success", "用户登录成功!");
return SUCCESS;
} else {
// 登录失败的错误信息
context.put("error", "用户名或密码错误,请重新登录!");
return ERROR;
}
}

3)修改登录页面输入框的name值。由于使用的是对象形式,name的值必须是模型对象名.属性名。

<form action="login" method="post">
用户名:<input type="text" name="user.username"/><br/>
密码:<input type="password" name="user.password"/><br/>
<input type="reset" value="重置"/>
<input type="submit" value="登录"/>
</form>

后台使用的模型对象名是user,故在页面也是需要使用user.前缀,否则无法获取。

6.2模型驱动

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

1)LoginAction类实现接口ModelDriven,此时此类不需要提供get和set方法

public class LoginAction extends ActionSupport implements ModelDriven<User> {

    private User user = new User();

    @Override
    public User getModel() {
        return user;
    }

    public String login(){
        ActionContext context=ActionContext.getContext();
        if ("admin".equals(user.getUsername()) && "123456".equals(user.getPassword())) {
            // 将用户名和密码信息放入context对象中
            context.put("username", user.getUsername());
            context.put("password", user.getPassword());
            context.put("success", "用户登录成功!");
            return SUCCESS;
        } else {
            // 登录失败的错误信息
            context.put("error", "用户名或密码错误,请重新登录!");
            return ERROR;
        }
    }


}

需要注意的是,使用这种方式,模型对象(如User)必须提供有参和无参构造。

2)login.jsp表单不需要使用user.前缀,只需要和User对象的属性名称保持一致即可

<form action="login" method="post">
    用户名:<input type="text" name="username"/><br/>
    密码:<input type="password" name="password"/><br/>
    <input type="reset" value="重置"/>
    <input type="submit" value="登录"/>
</form>

通过这三种方式的对比,各有优势,不过推荐使用模型驱动的方式,只需要保证表单属性和对象的属性一一对应即可。

7.struts2拦截器

7.1概述及原理

拦截器(Interceptor)是 Struts2 框架的核心组成部分,它类似于 Servlet 中的过滤器。拦截器链是指对应各个功能的拦截器按照一定的顺序排列在一起形成的链,而拦截器链组成的集合就是拦截器栈。当有适配连接器栈的访问请求进来时,这些拦截器就会按照之前定义的顺序被调用。拦截器的工作方式如下图:

 

 在执行 Action 的 execute() 方法之前会执行一次拦截,在 Action 和 Result 执行之后,拦截器会再次执行。在此链式执行的过程中,每一个拦截器都可以直接返回,停止后面的执行。

7.2拦截器的使用

拦截器的代码在struts2-interceptor目录下,源码请在第二章下载。

1)新建一个maven的web项目,添加java和resources目录

2)导入依赖

       <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm</artifactId>
            <version>7.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm-commons</artifactId>
            <version>7.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm-tree</artifactId>
            <version>7.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.struts</groupId>
            <artifactId>struts2-core</artifactId>
            <version>2.3.37</version>
        </dependency>
        <dependency>
            <groupId>org.apache.struts.xwork</groupId>
            <artifactId>xwork-core</artifactId>
            <version>2.3.37</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.30</version>
        </dependency>
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.8.1</version>
        </dependency>
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.24.0-GA</version>
        </dependency>
        <dependency>
            <groupId>ognl</groupId>
            <artifactId>ognl</artifactId>
            <version>3.0.21</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.13.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.13.3</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.18</version>
        </dependency>

3)在web.xml配置拦截器 

  <!-- 配置Struts2核心过滤器 -->
  <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>

4)创建用户实体类User

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private String username; // 用户名
    private String password; // 密码
}

5)创建登录 Action

package com.zxh.controller;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.zxh.entity.User;

public class LoginAction extends ActionSupport implements ModelDriven<User> {


    private User user = new User();

    @Override
    public User getModel() {
        return user;
    }

    public String login(){
        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.put("msg", "用户名或密码错误,请重新登录!");
            return INPUT;
        }
    }
}

6)创建用户Action

package com.zxh.controller;

import com.opensymphony.xwork2.ActionSupport;

public class UserAction  extends ActionSupport {

    public String add(){
        return SUCCESS;
    }

    public String list(){
        return SUCCESS;
    }

    public String update(){
        return SUCCESS;
    }

    public String delete(){
        return SUCCESS;
    }
}

7)创建拦截器

package com.zxh.config;

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 {

    @Override
    public String intercept(ActionInvocation actionInvocation) throws Exception {
        ActionContext context = actionInvocation.getInvocationContext();
        //获取用户登录信息
        Object username = context.getSession().get("username");
        if (null == username) {
            context.put("msg", "您还未登录,请先登录!");
            //返回登录请求
            return Action.LOGIN;
        } else {
            // 放行,向下执行
            return actionInvocation.invoke();
        }
    }
}

8)配置struts.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- 指定 Struts2 配置文件的 DTD 信息 -->
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
        "http://struts.apache.org/dtds/struts-2.3.dtd">
<!-- Struts2配置文件的根元素 -->
<struts>

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

    <!-- Struts2的Action必须放在指定的包空间下定义 -->
    <package name="hello" namespace="/" extends="struts-default">
        <!-- 声明拦截器和拦截器栈-->
        <interceptors>
            <interceptor name="privilege"
                         class="com.zxh.config.PrivilegeInterceptor" />
            <interceptor-stack name="myStack">
                <interceptor-ref name="defaultStack" />
                <interceptor-ref name="privilege" />
            </interceptor-stack>
        </interceptors>


        <action name="login" class="com.zxh.controller.LoginAction" method="login">
            <result name="success">/index.jsp</result>
            <result name="input">/login.jsp</result>
        </action>

        <action name="userAction_*" class="com.zxh.controller.UserAction" method="{1}">
            <!-- 定义处理结果和视图资源之间的映射关系 -->
            <result name="success">/{1}.jsp</result>
            <result name="login">/login.jsp</result>
            <!-- 使用拦截器 -->
            <interceptor-ref name="myStack" />
        </action>
    </package>
</struts>

9)修改index.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>
    <a href="userAction_add.action">添加用户</a><br>
    <a href="userAction_list.action">查询用户</a><br>
    <a href="userAction_update.action">修改用户</a><br>
    <a href="userAction_delete.action">删除用户</a><br>
</body>
</html>

10)创建登录页面login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>用户登录</title>
</head>
<body>
<div align="center">
    <form action="login" method="post">
        用户名:<input type="text" name="username"/><br/>
        密码:<input type="password" name="password"/><br/>
        <input type="reset" value="重置"/>
        <input type="submit" value="登录"/></br>
        <span style="color:#F00">${requestScope.msg}</span>
    </form>
</div>
</body>
</html>

11)创建增删改查的四个页面

-----------list.jsp------
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    用户列表页面
</body>
</html>


-----------add.jsp------
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    用户添加页面
</body>
</html>


-----------update.jsp------
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    用户修改页面
</body>
</html>




-----------delete.jsp------
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    用户删除页面
</body>
</html>

12)启动运行。先访问localhost:8080/index.jsp,点击添加用户,会直接跳转到登录页面,显示未登录信息。输入admin,123456进行登录,返回到了index.jsp,再次点击添加用户,即可进入添加的页面。其他几个页面也是同样的效果。

注意:在拦截器中,必须使用session才能获取到用户登录的信息,因此在登录action中登录成功时要把用户信息存到session中。而jsp页面只能通过requestScope获取ActionContext的值,因此页面显示的提示信息需要存到ActionContext对象中。

8.struts2标签

struts2不仅包含业务逻辑控制器,还有一个重点是页面视图。而视图部分有自带的标签进行实现。

8.1标签的分类

8.1.1分类

struts2的标签可分为2大分,细分的话又可以分为5小类,看下图:

 

8.1.2用法

要使用struts2的标签,需要在jsp页面导入其标签库

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

导入之后,使用s前缀和标签名使用,如<s:if>标签。

8.2.3标签主题

对于Struts2的页面来说,它也通过不同的模生成了不同的主题,也就是说,不同的主题就是不同的样式。其提供的主题如下:

名称作用
simple 最简单的主题,使用该主题时,每个 UI 标签只生成最基本的 HTML 元素,没有任何附加功能。
xhtml Struts2 的默认主题,提供了布局功能、Label 显示名称以及与验证框架和国际化框架的集成。
css_xhtml 对 xhtml 的扩展,在 xhtml 的基础之上添加对 CSS 的支持和控制。
Ajax 继承自 xhtml,提供 Ajax 支持。

xhtml是Struts2的默认主题,那么如何更换主题呢?很简单,只需要在struts.xml中配置常量值即可:

<constant name="struts.ui.theme" value="simple"/>

8.2数据标签

数据标签主要用于提供各种和数据访问相关的功能。在webapp下新建example1.jsp,数据标签的示例均值此文件中说明,其初始内容如下:

 

8.2.1 <s:property> 标签

输出指定的value值。其属性如下表:

属性值 是否必选 说明
value 定需要输出的属性值,如果没有指定该属性,则默认输出 ValueStack 栈顶的值
id 指定该元素的标识
default 果要输出的属性值为 null,则显示 default属性的指定值
escape 指定是否忽略 HTML 代码,true忽略,false不忽略。默认值是 true

1)在jsp中使用如下代码,展示数据

<p>property标签</p>
直接输出字符串:<s:property value="'今天的天气还不错'"/><br/>
使用默认值:<s:property value="" default="value是null时我会显示"/><br/>
忽略HTML代码:<s:property value="'<H3>你今天聊天了吗?</H3>'" escape="true"/><br/>
不忽略HTML代码:<s:property value="'<H3>你今天聊天了吗?</H3>'" escape="false"/><br/>

2)效果图如下:

8.2.2 <s:a> 标签

此标签和<a>标签类型,表示超链接。

1)代码

<p>a标签</p>
<s:a href="www.baidu.com">点我去百度</s:a>

2)截图

8.2.3 <s:debug> 标签

输出服务端对象中的信息。使用此标签后,会在页面生成一个debug的标签,点击后即可看到相关的信息。

1)代码

<p>debug标签</p>
<s:debug/>

2)截图

8.2.4 <s:include> 和<s:param>标签

<s:include> 标签用于用于在当前页面中包含另一个 Web 资源,而<s:param> 标签可以给其传递参数。

1)在webapp下新建myTest.jsp,内容如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>我是test页面</title>
</head>
<body>
    <h2>我是test页面</h2>
    <p>传递的参数1是:<%out.print(request.getParameter("username")); %></p>
    <p>传递的参数2是:${param.date }</p>
</body>
</html>

2)在example1.jsp使用标签

<p>include标签</p>
<s:include value="myTest.jsp">
<s:param name="username" value="'钟小嘿'"></s:param>
<s:param name="date">2021年4月10号</s:param>
</s:include>

3)效果截图

4)分析

第一:如果<s:include> 不需要传递参数,那么就不需要使用<s:param> 标签;

第二:<s:param> 标签中name属性是必选的,而指定value的方式有两种,可以使用value属性,此时需要注意字符串的单引号问题;也可以在标签里面写内容;

第三:在获取参数的值时,也有两种方式,在实例中已经体现。对于out.print(request.getParameter())若值不存在则为其属性赋值为 null,而$param若值不存时就不显示。

8.3控制标签

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

<s:if>、<s:elseif>、<s:else> 标签与 Java 中的 if、else if 和 else 语句功能类似,用于程序的分支逻辑控制。而 <s:if> 标签可以单独使用,而 <s:elseif>、<s:else> 都必须与 <s:if> 标签结合才能使用。

<s:if>、<s:elseif>标签必须指定 test 属性,该属性用于设置标签的判断条件,其值是一个 boolean 类型的条件表达式。

1)代码

为了演示,在myTest.jsp中使用此标签判断值

<h2>
<s:if test="param.date==null">222</s:if>
<s:else>333</s:else>
</h2>

2)截图

 在此子页面中,通过判断param中的date值进行控制显示,由于date的值不是null,因此显示333。若date是null,则会显示222。

8.3.2 <s:iterator>标签

遍历集合。使用var定义集合中的每一个元素,使用status定义状态,就可以获取索引值。

1)代码

<p>iterator标签</p>
<s:iterator var="item" value="{'java','C#','paython','php'}" status="st">
<li><s:property value="#st.index+':'"/><s:property value="item"/></li>
</s:iterator>

2)效果图

8.4表单标签

8.4.1<s:textfield>、<s:textarea>标签

二者都用于创建文本框,其主要区别在于 <s:textfield> 创建的是单行文本框,而 <s:textarea> 创建的是多行文本框,示例代码在<s:form>中统一编写。

<s:textfield label="用户名" name="username"/>

<s:textarea label="描述" name="description"/>

8.4.2<s:passWord>标签

用于创建密码输入框,name指定密码输入框的名称,size指定密码输入框的显示宽度,maxlength限定密码输入框的最大输入字符串个数,showPassword是否显示初始值,若显示也是密文,用掩码代替

<s:password label="password" name="password" maxlength="20"/>

8.4.3<s:radio>标签

用于创建单选按钮,list指定生成单选框中的集合,listKey指定集合对象中的哪个属性作为选项的 value,listValue指定集合对象中的哪个属性作为选项的内容

<s:radio name="" label="性别" list="#{0:'男',1:'女'}" listKey="key" listValue="value" value="0"/>

8.4.4<s:select>标签

用于创建一个下拉列表框,其属性值如下表

属性名说明
list 用于生成下拉框的集合
listKey 生成选项的 value 属性
listValue 生成选项的显示文字
headerKey 在所有的选项前再添加一个额外的选项作为其标题的 value 值
headerValue 显示在页面中 header 选项的内容
multiple 指定是否多选,默认为 false
emptyOption 是否在标题和真实的选项之间加一个空选项
size 下拉框的高度,即最多可以同时显示多少个选项

headerKey 和 headerValue 属性需要同时使用,使用时会在所有的真实选项之前加添一项作为标题项。

<s:select label="所在城市" name="city"
          list="#{'BJ':'北京','SH':'上海' }"
          listKey="key" listValue="value"/>

8.4.5<s:optgroup>标签

用于生成选项组,通常作为 <s:select> 标签的子标签使用,也就是二级选择,其属性值和<s:select>相似。

<s:select label="请选择所在城市" name="city"
  list="#{'BJ':'北京','SH':'上海' }"
  listKey="key" listValue="value">
  <s:optgroup label="河北"
   list="#{'BD':'保定','HS':'衡水','SJZ':'石家庄'}"
   listKey="key" listValue="value"/>
  <s:optgroup label="河南"
   list="#{'LY':'洛阳','KF':'开封','SQ':'商丘'}"
   listKey="key" listValue="value"/>
</s:select>

8.4.6<s:checkboxlist>标签

用于一次性创建多个复选框,其属性值如下表

属性名说明
name 指定该元素的 name
label 指定复选框前显示的文本
list 定义集合变量
listKey 指定集合对象中的哪个属性作为选项的 value
listValue 指定集合对象中的哪个属性作为选项的内容
<s:checkboxlist label="爱好" name="hobby" list="#{1:'足球',2:'篮球',3:'读书',4:'运动'}" listKey="key" listValue="value"></s:checkboxlist>

8.4.7<s:file>标签

用于创建一个文件选择框

<s:file name="uploadFile" accept="text/*"/>
<s:file name="otherUploadFile" accept="text/html,text/plain"/>

8.4.8<s:hidden>标签

用于创建隐藏表单元素

<s:hidden name="id" value="1"/>

8.4.9<s:submit>标签

用于产生 HTML 中的提交按钮,通常与 <s:form> 标签一起使用。action指定提交时对应的 Action,method指定 Action 中调用的方法。

<s:submit value="注册"/>

8.4.10<s:reset>标签

用于创建一个重置按钮

<s:reset value="reset"/>
<s:reset name="reset" value="重置"/>

8.4.11<s:form>标签

用于创建一个表单。需要综合上述的部分标签进行使用。

代码:

<s:form action="login" method="post">
<s:textfield label="用户名" name="username"/>
<s:password label="密码" name="password" maxlength="20"/>
<s:radio name="" label="性别" list="#{0:'男',1:'女'}" listKey="key" listValue="value" value="0"/>
<s:select label="所在城市" name="city"
list="#{'BJ':'北京','SH':'上海' }"
listKey="key" listValue="value">
<s:optgroup label="河北"
list="#{'BD':'保定','HS':'衡水','SJZ':'石家庄'}"
listKey="key" listValue="value"/>
<s:optgroup label="河南"
list="#{'LY':'洛阳','KF':'开封','SQ':'商丘'}"
listKey="key" listValue="value"/>
</s:select>
<s:checkboxlist label="爱好" name="hobby" list="#{1:'足球',2:'篮球',3:'读书',4:'运动'}" listKey="key" listValue="value"></s:checkboxlist>
<s:textarea label="备注" name="remark"/>
<s:file label="照片"></s:file>
<s:submit value="注册"/>
<s:reset name="reset" value="重置"/>
</s:form>

截图:

9.struts2的OGNL

9.1基本概念

表达式:表达式是整个 OGNL 的核心,OGNL 会根据表达式到对象中取值。也就是%、$、#三个符号在页面的使用

上下文对象:context 对象是一个 Map 类型的对象,在表达式中访问 context 中的对象,需要使用 # 号加对象名称

根对象:OGNL 的操作对象,OGNL 可以对根对象进行取值或写值等操作

9.2 #符号

由于$已在EL表达式广泛应用,在struts.xml也有使用,在此不再赘叙。#的用途如下:

9.2.1访问非根对象的属性

由于 Struts2 中值栈被视为根对象,所以访问其他非根对象时,需要加#前缀。

例如‘#session.user’表达式相当于ActionContext.getContext().getSession().getAttribute("user")

#request.userName’表达式相当于 request.getAttribute("userName")。

9.2.2用于过滤和投影集合

如books.{?#this.price>25}。

9.2.3构造 Map

如 #{key1:value1,key2:value2},这种方式常用于给 radio 或 select、checkbox 等标签赋值。

9.3  % 符号

在标签的属性值被理解为字符串类型时,告诉执行环境‘%{}’中的是 OGNL 表达式,并计算 OGNL 表达式的值。

9.4实战演练

1)新建Olgn的action

package com.zxh.controller;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.ServletActionContext;

import javax.servlet.http.HttpServletRequest;

public class OgnlController  extends ActionSupport {

    public String execute(){
        ActionContext context = ActionContext.getContext();
        HttpServletRequest request = ServletActionContext.getRequest();
        context.getSession().put("msg","我是session信息");
        context.getApplication().put("msg","我是application信息");
        request.setAttribute("msg","我是request信息");
        return SUCCESS;
    }
}

2)新建ognl.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>ognl</title>
</head>
<body>
    <p>session.msg:<s:property value="#session.msg"></s:property></p>
    <p>application.msg:<s:property value="#application.msg"></s:property></p>
    <p>request.msg:<s:property value="#request.msg"></s:property></p>
    <p>attr.msg:<s:property value="#attr.msg"></s:property></p>
    <hr />
    <h3>%符号的用法</h3>
    <h4>构造Map</h4>
    <s:set name="key" value="#{'key1':'value1','key2':'value2'}" />
    <p>key1的值是 <s:property value="#key['key1']" /></p>
    <p>不使用%:<s:url value="#key['key1']" /></p>
    <p>使用%:<s:url value="%{#key['key1']}" /></p>
    <hr />
</body>
</html>

3)在xml配置action

<action name="ognl" class="com.zxh.controller.OgnlController" >
<result>/ognl.jsp</result>
</action>

4)启动项目,访问http://localhost:8080/ognl,截图如下:

9.5 OGNL其他功能

除了在 JSP 页面中可以使用 OGNL 表达式以外,OGNL 还支持在 Java 代码中访问对象方法和静态方法,下面一一说明。

9.5.1使用Ognl.getValue()获取对象的属性

新建一个main方法,进行测试

   public static void main(String[] args) {
        User user = new User();
        user.setUsername("张三");
        try {
            System.out.println(ognl.Ognl.getValue("username", user));
        } catch (OgnlException e) {
            e.printStackTrace();
        }
    }

执行后控制台打印的就是张三,getValue方法需要指定属性名称和对象。

9.5.2访问静态方法和静态属性

OGNL 同时支持静态方法和静态属性的调用,其语法格式如下所示:

@类的全路径名@属性名称
@类的全路径名@方法名称(参数列表)

需要注意的是,在低版本的 Struts2 中,已经默认开启了对访问类静态方法的支持,但是高版本的 Struts2 默认是关闭这项支持的,在 struts.xml 中进行如下配置:

<constant name="struts.ognl.allowStaticMethodAccess" value="true"/>

在TestMain方法中添加静态属性和方法,

public static String staticValue="我是静态值,hello";
public static void testMethod(){
System.out.println("我是静态方法,我被执行了");
}

在ognl.jsp中使用静态属性、调用静态方法

获取的静态属性值为:<s:property value="@com.zxh.controller.TestMain@staticValue"/><br/>
调用静态方法的结果请查看控制台<s:property value="@com.zxh.controller.TestMain@testMethod()"/>

重启项目,访问http://localhost:8080/ognl,静态属性会展示在页面上,静态方法也执行了,打印信息可看控制台。

10.SSH项目整合

SSH代表的是Struts2,Spring和Hibernate,虽然目前用的比较少,但可以学习一下。

代码在ssh-demo目录下,源码请在上面下载,整合步骤如:

1)新建maven的web项目,添加java和resources目录

2)导入依赖

3)配置web.xml

4)创建index.jsp页面

5)创建实体对象User

6)创建UserAction

7)创建UserService及实现类

8)创建UserDao接口及实现类

9)创建User.hbm.xml

10)创建db.properties

11)创建log4j2.xml

12)创建applicationContext.xml

13)创建struts.xml

14)启动项目,访问登录页面,输入正确的用户名和密码可跳转到首页。

posted @ 2021-04-20 19:14  钟小嘿  阅读(182)  评论(0编辑  收藏  举报