Struts2

Struts2框架学习

第一讲:回顾

  • Web开发的进程有两种模式,model1开发模式和model2开发模式。
  • Model1模式
    • 优点:执行效率高,开发效率高,适合中小型项目。
    • 缺点:逻辑比较混乱,页面混乱,维护困难,扩展不容易。
  • Model2
    • 优点:解耦,将试图层和业务层分离。结构清晰,分工明确,维护方便,适合中大型项目。
    • 缺点:执行效率相对Model1低,代码量大,重复代码比较多。
    • Model2又称为MVC设计模式。
    • M:model模型
    • V:view试图
    • C:controller控制
  • 使用Servlet的MVC存在一些问题,有重复的代码—通过MVC框架来解决。
  • Servlet解决了什么问题
    • 将url映射到一个java类的处理方法上。
    • 接收请求的数据。
    • 如何将处理结果显示到页面。
    • 如何进行页面的跳转。

      第二讲:框架

  • Java学习路线
    • 基础语法—->方法(目的:减少代码,代码重复利用)—->类(目的:代码复用,OOP:面向对象)—-> jar包(多个类封装为jar包。目的:代码复用)—-> 框架(一个或多个jar包。目的:代码重用)
    • 从中体现最多的思想是:封装
  • 什么是框架?
    • framework
    • 框架是模板,模型,模子。
    • 专业语言描述:框架是一个可重用的半成品。
  • 为什么要学框架?
    • 提高开发效率,降低学习难度。
  • 如何学习框架?
    • 框架是别人提供的,那么使用框架时,要遵守框架提供的规。
    • 学习框架就是学习框架的规则。
    • 框架有两个部分组成:可变的部分和不可变的部分。
  • 常见的框架有哪些
    • struts2,strut1,spring,hibernate,Mybatis,Shiro,nutz等待。

      第三讲:Struts入门

  • 什么是struts?
    • Apache Struts is a free, open-source, MVC framework for creating elegant, modern Java web applications. It favors convention over configuration, is extensible using a plugin architecture, and ships with plugins to support REST, AJAX and JSON.
  • Struts2是一个开源、免费、轻量级的mvc框架
    • 轻量级:如果一个框架没有侵入性,就说该框架是轻量级的。
    • 侵入性:如果使用一个框架必须实现框架提供的接口,或者继承框架提供的类。
  • 在Struts2之前是Struts1。Struts1出现很早,市场占有率比较高,所以不支持一些新的试图层展示技术,逐渐被淘汰。Struts2=Struts1+webwork
  • Struts2是基于请求的mvc框架。
  • Struts2的目录结构
    • apps:应用案例。
    • docs:文档。
    • lib:使用的库包。
    • src:源码。
  • Struts2的Hello World案例
    • 新建web项目
    • 导入jar包
    • 编写web.xml文件–配置struts2的前端控制器(分发器)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <!-- 配置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>*.action</url-pattern>
    </filter-mapping>
  • 编写业务处理类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package demo;
/***
* struts2的第一个案例
* @author 朱楚利
*
*/
public class HelloWorld {
/***
* 在Struts2中,所有的业务方法都是public
* 返回值都为String类,所有的业务方法都没有参数
* 方法名可以自定义,默认是execute()
* @return
*/
public String execute() {
System.out.println("Hello struts2");
return "success";
}
}
  • 在src下,添加strut2的配置文件,名称为为struts.xml,该配置文件名不能更改,并进行配置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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" namespace="/" extends="struts-default">
<!--
配置action
配置url和处理类的方法进行映射
-->
<action name="hello" class="demo.HelloWorld">
<result>/hello.jsp</result>
</action>
</package>
</struts>
  • 编写hello.jsp页面
1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>hello world struts2</h1>
</body>
</html>
  • 发布项目并进行测试。

第四讲 配置讲解

  • web.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     <!-- 
    配置struts2的前端控制器
    struts2框架开始工作的入口
    接管请求
    -->
    <filter>
    <!-- 名称自定义 -->
    <filter-name>struts2</filter-name>
    <!-- Struts2提供的控制器类 -->
    <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>
    <filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>*.action</url-pattern>
    </filter-mapping>
  • struts2.xml 该配置文件的文件名固定,并且只能放在src目录下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
       <struts>
    <!--
    package 分模块管理
    name属性属性值自定义但是不能重复,在一个项目中是唯一的
    namespace属性命名空间与url请求路径直接相关
    如:“/” 请求为 /hello.action;
    如:"/user",则请求路径为/user/hello.action
    extends 继承 必须直接或间接继承struts-default
    result的请求路径是相对namespace的属性值
    -->
    <package name="default" namespace="/" extends="struts-default">
    <!--
    配置action
    配置url和处理类的方法进行映射
    name为请求名称 不加后缀
    class 处理类的完全限定名称 包名+类名,如果不配置,由默认类来处理
    com.opensymphony.xwork2.ActionSupport
    method指定处理请求的方法,默认是execute()方法
    -->
    <action name="hello" class="demo.HelloWorld" method="hello">
    <!--
    result结果集配置
    name 结果集名称,和处理方法的返回之匹配 默认为success,可以自定义。
    Struts2提供了5个返回结果:
    Action.SUCCESS: 执行成功,跳转到下一个试图
    Action.NONE:执行成功,不需要试图展示
    Action.ERRPR:执行失败,显示失败页面
    Action.INPUT:要执行该Action需要更多的输入条件
    Action.LOGIN:需要登陆后才能执行
    type指定响应结果类型
    dispatcher 请求转发,默认使用
    redirect 请求重定向
    redirectAction 重定向到另一个action
    result的值为跳转页面 不加"/"为相对namespace路径,建议使用绝对路径
    -->
    <result name="" type="">/hello.jsp</result>
    </action>
    </package>

第五讲 Struts2的执行流程

  • 浏览器发起请求 –> 服务器接收请求并交给Struts2的前端控制器 —–> 根据请求的url查看struts.xml中的namespace+actionName是否存在 –> 执行action所对应的类的对应方法 —> 根据方法的执行结果到action的结果集进行匹配 –> 响应结果。
  • Struts2

avator.png

第六讲 Struts2的数据处理一

  • 在struts2中,对表单数据的处理有三种:属性驱动,对象驱动,模型驱动
  • 使用Strut2获取表达数据:只需要表单域数据名称和Action处理类的属性名称一致,并且提供属性的set方法,那么在Action处理类中即可获取表单数据。这种获取数据的方式称为属性驱动
  • 处理类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package demo;

import com.opensymphony.xwork2.Action;

/***
* Struts2数据处理案列
* @author 1huangzewei
*
*/
public class LoginAction {

private String username;
private String password;
//登陆处理业务
public String login() {
System.out.println("username="+username+";password="+password);
return Action.SUCCESS;
}

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



}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Struts2的数据处理</title>
</head>
<body>
<form action="login.action" method="post">
username:<input type="text" name="username"/><br>
password:<input type="password" name="password"/><br>
<input type="button" value="登录"/>
</form>
</body>
</html>
1
2
3
4
5
6
7
8
9
<?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="login" namespace="/" extends="struts-default">
<action name="login" class="demo.LoginAction" method="login">
<result>/success.jsp</result>
</action>
</package>
</struts>
  • 如果数据需要显示在页面上,那么该数据可以作为处理类的数据,处理方法后该属性有值,并且有该属性的get方法,那么在页面上可以直接通过el表达式获取。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package demo;

import com.opensymphony.xwork2.Action;

/***
* Struts2数据处理案列
* @author 1huangzewei
*
*/
public class LoginAction {

private String username;
private String password;
//登陆处理业务
public String login() {
System.out.println("username="+username+";password="+password);
return Action.SUCCESS;
}

public String getUsername() {
return username;
}

public String getPassword() {
return password;
}

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

}
1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ page language="java" contentType="text/html; charset=utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>成功页面</title>
</head>
<body>
<h1>My First Struts Demo</h1>
<h3>恭喜${username}登录成功<h3>
<h2>${password}<h2>
</body>
</html>

第七讲 数据处理二

IOC:控制反转

  • 对象驱动方式获取属性:(重点)
    • 在action的处理类中,属性以对象的形式存在,该属性对象只需声明并为其提供get方法和set方法即可,需要保证该属性对象有无参构造方法。
    • 在表单域中的表单域名称以属性对象.属性对象的属性来命名。 这种方式比较重要
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//User类
package entity;

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;
}

}
//处理类
package demo;

import com.opensymphony.xwork2.Action;

import entity.User;

/***
* Struts2数据处理案列
* @author 1huangzewei`
*
*/
public class LoginAction02 {
//保证对象要有无参的构造方法
private User user; //IOC方式
//登陆处理业务
public String login() {
System.out.println("username="+user.getUsername()+"\tpassword="+user.getPassword());
return Action.SUCCESS;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!--显示成功页面-->
<%@ page language="java" contentType="text/html; charset=utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>成功页面</title>
</head>
<body>
<h1>My First Struts Demo</h1>
<!-- 属性驱动 -->
<!--<h3>恭喜${username}登录成功<h3>
<h2>${password}<h2>-->
<!-- 对象驱动 -->
<h3>恭喜${user.username}登录成功<h3>
<h2>${user.password}<h2>
</body>
</html>
<!--登录页面-->
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Struts2的数据处理</title>
</head>
<body>
<form action="login02.action" method="post">
username:<input type="text" name="user.username"/><br>
password:<input type="password" name="user.password"/><br>
<input type="button" value="登录"/>
</form>
</body>
</html>
  • 模式驱动:在对象驱动中,页面的表单域名称比较复杂,如果对象属性比较多的情况下,代码量比较大。通过模式驱动可以解决这个问题。
  • 模式驱动需要实现ModelDriver接口,并且主动将对象创建好。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//处理类
package demo;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ModelDriven;

import entity.User;

/***
* Struts2数据处理案列:模型驱动
* @author 1huangzewei
*
*/
public class LoginAction03 implements ModelDriven<User>{
//保证对象要有无参的构造方法
private User user=new User(); //IOC方式
//登陆处理业务
public String login() {
System.out.println("username="+user.getUsername()+"\tpassword="+user.getPassword());
return Action.SUCCESS;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public User getModel() {
// TODO Auto-generated method stub
return user;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!--登录页面-->
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Struts2的数据处理</title>
</head>
<body>
<form action="login02.action" method="post">
username:<input type="text" name="username"/><br>
password:<input type="password" name="password"/><br>
<input type="button" value="登录"/>
</form>
</body>
</html>
<!--显示页面-->
<%@ page language="java" contentType="text/html; charset=utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>成功页面</title>
</head>
<body>
<h1>My First Struts Demo</h1>
<h3>恭喜${username}登录成功<h3>
<h2>${password}<h2>
</body>
</html>

第七讲 数据处理二

IOC:控制反转

  • 对象驱动方式获取属性:(重点)
    • 在action的处理类中,属性以对象的形式存在,该属性对象只需声明并为其提供get方法和set方法即可,需要保证该属性对象有无参构造方法。
    • 在表单域中的表单域名称以属性对象.属性对象的属性来命名。 这种方式比较重要
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//User类
package entity;

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;
}

}
//处理类
package demo;

import com.opensymphony.xwork2.Action;

import entity.User;

/***
* Struts2数据处理案列
* @author 1huangzewei
*
*/
public class LoginAction02 {
//保证对象要有无参的构造方法
private User user; //IOC方式
//登陆处理业务
public String login() {
System.out.println("username="+user.getUsername()+"\tpassword="+user.getPassword());
return Action.SUCCESS;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--显示成功页面-->
<%@ page language="java" contentType="text/html; charset=utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>成功页面</title>
</head>
<body>
<h1>My First Struts Demo</h1>
<!-- 属性驱动 -->
<!--<h3>恭喜${username}登录成功<h3>
<h2>${password}<h2>-->
<!-- 对象驱动 -->
<h3>恭喜${user.username}登录成功<h3>
<h2>${user.password}<h2>
</body>
</html>

第八讲 action的几种创建方式

  • Struts2中有三种方式来创建action处理类
    • 实现action接口的方式来创建处理类,好处:
      • 可以直接使用Action类提供的常量
      • 必须重写默认的处理方法。
      • 这种方法使用比较少。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.zhuchuli.action;

import com.opensymphony.xwork2.Action;

/***
* 通过实现Action接口来创建处理类:好处
*实现action接口的方式可以使用Action提供的常量
*必须重写默认处理方法
*这种方式使用的比较少
* @author 1huangzewei
*
*/
public class HelloAction implements Action{

@Override
public String execute() throws Exception {
System.out.println("action execute");
return SUCCESS;
}

}
  • 通过继承ActionSupport类来创建Action的处理类,Strut2推荐使用这种方式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.zhuchuli.action;

import com.opensymphony.xwork2.ActionSupport;

/***
* 通过继承ActionSupport创建Action处理类:好处
* 并且ActionSupport类提供了很多其他Struts2的功能。
* 比如:数据校验,国际化
* 内部已经提供了默认实现方法
*
* @author 1huangzewei
*
*/
public class HelloAction02 extends ActionSupport{


}
  • 无侵入性的实现方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.zhuchuli.action;

import com.opensymphony.xwork2.Action;

/***
* 无侵入性的实现方式
* @author 1huangzewei
*
*/
public class Hello02Action {

public String execute() {
System.out.println("无侵入性的实现方式");
return Action.SUCCESS;
}
}

第九讲 Struts2配置讲解2

  • constant标签
1
2
3
4
5
6
7
   <!-- strut2的常量配置 -->
<!-- 扩展名的配置 -->
<constant name="struts.action.extension" value="do,zhangsan,,action"></constant>
<!-- 设置页面字符集,页面解决 -->
<constant name="struts.i18n.encoding" value="utf-8"></constant>
<!-- 开发模式 -->
<constant name="struts.devMode" value="true"></constant>
  • include标签:引入其他的配置文件,在团队开发中使用
1
2
3
4
5
6
7
<?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>

<include file="struts2/conguration/system.xml"></include>

</struts>

第十讲 action的优化配置

  • 在Struts2中,随着业务的增加,action的配置文件会急剧增加,导致了配置文件膨胀的问题。struts2中提供了三种方式来解决这个问题。
    • 通过动态方法调用来解决。
    • 通过通配符来解决。
    • 通过注解来解决。
  • 传统的action方式,配置文件会变得很庞大
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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" namespace="/" extends="struts-default">
<action name="save" class="com.zhuchuli.action.UserAction" method="save">
<result type="redirectAction">find</result>
</action>
<action name="delete" class="com.zhuchuli.action.UserAction" method="delete">
<result type="redirectAction">find</result>
</action>
<action name="update" class="com.zhuchuli.action.UserAction" method="update">
<result type="redirectAction">find</result>
</action>
<action name="query" class="com.zhuchuli.action.UserAction" method="query">
<result>/list.jsp</result>
</action>
</package>

</struts>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.zhuchuli.action;

import com.opensymphony.xwork2.Action;

public class UserAction {
//增加
public String save() {
return Action.SUCCESS;
}
//删除
public String delete() {
return Action.SUCCESS;
}
//修改
public String update() {
return Action.SUCCESS;
}
//查询
public String query() {
return Action.SUCCESS;
}
}
  • 通过动态方法解决该问题
    • 在常量中开启动态方法调用。<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
    • 配置action,一个Action处理类,只需配置一次。不需要配置处理方法。
1
2
3
4
5
6
<action name="userAction" class="com.zhuchuli.action.UserAction">
<result type="redirect">userAction!query.action</result>
<result type="redirect">userAction!delete.action</result>
<result name="list">/list.jsp</result>
<result name="delete">/delete.jsp</result>
</action>
  • 调用处理方法,ActionName!method.action,例如:http://localhost:8080/Struts_study02/userAction!delete.action
  • 注意:使用动态方法调用需要不同处理方法的返回值是否一致问题。会配置多个结果集。这种方式不推荐使用,不安全。
1
2
3
4
5
6
7
8
<!-- *表示一个或多个字符 -->
<!-- 占位符{1}表示匹配第一个“*”的内容 -->
<!-- 使用通配符配置时,需要注意如果有不是通配符的action,先匹配action再匹配通配符-->
<!-- 在实际应用开发中,使用通配符比较常见 -->
<action name="*" class="com.zhuchuli.action.UserAction" method="{1}">
<result type="redirect">query</result>
<result>/list.jsp</result>
</action>
  • 注意如果使用通配符配置,那么需要注意不同处理方法的返回值的问题。
  • 使用注解来解决。
  • 使用注解需要导入struts2-convention-plugin-2.3.37.jarasm-3.3.jarasm-commons-3.3.jarasm-tree-3.3.jar
  • 在处理类上填写相关注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.zhuchuli.action;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;


@ParentPackage(value="struts-default")
@Namespace(value="/")
public class UserAction {
//增加
@Action(value="save",results= {
@Result(location="/list.jsp")
})
public String save() {
System.out.println("save");
return "success";
}
//删除
public String delete() {
System.out.println("delete");
return "success";
}
//修改
public String update() {
System.out.println("update");
return "success";
}
//
//查询
@Action(value="query",results= {
@Result(name="list",location="/list.jsp")
})
public String query() {
System.out.println("query");
return "list";
}
}

第十一讲 ThreadLocal和ActionContext

  • ThreadLocal是一个容器,存放在容器中的数据是线程安全的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package threadlocal;

public class ThreadLocalDemo {

public static void main(String[] args) {
final ThreadLocal<Integer> th=new ThreadLocal<Integer>();
//向ThreadLocal中存放数据
th.set(23);
new Thread(() -> {
th.set(12);
System.out.println("====================");
//获取存放在容器中的数据
System.out.println("thread-->"+th.get());//12
}) .start();
System.out.println("main -->"+th.get()); //23
}

}
  • ActionContext:是Struts2的上下文对象,它的本质是一个容器,也是一个Map结构的对象。ActionContext贯穿于整个Action生命周期,每次接受请求后都会新建一个ActionContext对象,将ServletAPI中的数据存入到ActionContext中,实现了struts2和Servlet的解耦,使得测试可以不依赖容器。而且ActionContext是线程安全的。由于每次请求都会重新创建ActionContext,导致执行效率比Servlet低。
  • ActionContext有六个对象:
    • request:存放的是HttpServletRequest域中的数据。
    • session:存放的是HttpSession域中的数据。
    • application:存放的是ServletContext域中的数据。
    • parameters:存放的是请求参数
    • attr:存放的是requestsessionapplication中的数据。
    • ValueStacke:业务处理类的相关数据。
1
2
3
4
5
6
7
8
// helpers to get access to request/session/application scope
extraContext.put("request", requestMap);
extraContext.put("session", sessionMap);
extraContext.put("application", applicationMap);
extraContext.put("parameters", parameterMap);

AttributeMap attrMap = new AttributeMap(extraContext);
extraContext.put("attr", attrMap);
  • ServletContext:是一个容器,是一个对象,相当于map。

第十二讲 ognl

  • ognlObject graph navigation language,对象导航图语言。
  • Struts2中,ognl完成数据设置和类型转换两个功能。
  • struts2中是通过ognl来设值和取值的。ActionContext作为ognl的上下文,ValueStack作为ognlroot对象
  • struts2中使用ognl表达式获取数据需要使用struts2的标签库,使用struts2的标签库,需要注意一定是通过过滤器后才能解析struts2的标签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package demo;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;

/***
* Struts2数据处理案列:属性驱动
* @author 1huangzewei
*
*/
public class LoginAction {

private String username;
private String password;
//登陆处理业务
public String login() {
System.out.println("username="+username+";password="+password);
ActionContext.getContext().getApplication().put("address", "北京尚学堂");
return Action.SUCCESS;
}

public String getUsername() {
return username;
}

public String getPassword() {
return password;
}

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



}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%@ page language="java" contentType="text/html; charset=utf-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>成功页面</title>
</head>
<body>
<h1>My First Struts Demo</h1>
登录成功,欢迎<s:property value="username"/><br>
<h2>${password}<h2>
<h3><s:property value="#application.address"/></h3>
<h3><s:property value="#parameters.username"/></h3>
</body>
</html>
  • ognl获取各种域中的数据如下:
    • application#application.username#application['username']
    • session#session.username#session['username']
    • request#request.username#request['username']
    • parameters#parameters.username#parameter['username']
    • attr#attr.username#attr['username'],按pageContext、request、session、application顺序查找。

第十三讲 ServletAPI

  • Struts2的开发中,依然需要使用到ServletAPI,比如用户登录之后,需要将用户信息保存到session中.在Struts2中有两种方式可以获取ServletAPI,一种解耦方式,一种是耦合方式。
    • 解耦的实现方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.zhuchuli.action;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;

import cn.zhuchuli.vo.User;

public class LoginAction {
//保证对象要有无参的构造方法
private User user;

public User getUser() {
return user;
}

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

public String login() {
System.out.println("username="+user.getUsername()+"\tpassword="+user.getPassword());
if("possible".equals(user.getUsername()) && "123456".equals(user.getPassword())) {
//用户登录成功,将用户信息写入到session
ActionContext.getContext().getSession().put("currentUser", user);
//获取request
Map<String,Object> req=(Map<String,Object>)ActionContext.getContext().get("request");
return Action.SUCCESS;
}
return Action.LOGIN;
}
}
  • 耦合的方式
    • 直接通过ServletContext获取
1
2
3
4
5
6
7
8
9
10
public String login() {
System.out.println("username="+user.getUsername()+"\tpassword="+user.getPassword());
if("possible".equals(user.getUsername()) && "123456".equals(user.getPassword())) {
//直接获取ServletAPI,所以是耦合的方式
HttpServletRequest req=ServletActionContext.getRequest();
req.getSession().setAttribute("currentUser", user);
return Action.SUCCESS;
}
return Action.LOGIN;
}
  • 通过ActionContext获取
1
2
3
4
5
6
7
8
9
10
11
public String login() {
System.out.println("username="+user.getUsername()+"\tpassword="+user.getPassword());
if("possible".equals(user.getUsername()) && "123456".equals(user.getPassword())) {
//通过ActionContext方式获取ServletAPI
System.out.println("通过ActionContext方式获取ServletAPI");
HttpServletRequest req=(HttpServletRequest) ActionContext.getContext().get(StrutsStatics.HTTP_REQUEST);
req.getSession().setAttribute("currentUser", user);
return Action.SUCCESS;
}
return Action.LOGIN;
}
  • 通过IOC方式直接获取到ServletAPI
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class LoginAction03 implements ServletRequestAware{
//保证对象要有无参的构造方法
private User user;
private HttpServletRequest req;
public User getUser() {
return user;
}

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

public String login() {
System.out.println("username="+user.getUsername()+"\tpassword="+user.getPassword());
if("possible".equals(user.getUsername()) && "123456".equals(user.getPassword())) ;
//通过IOC方法获取ServletAPI对象
System.out.println("通过IOC方法获取ServletAPI对象");
req.getSession().setAttribute("currentUser", user);
return Action.SUCCESS;
}
return Action.LOGIN;
}

@Override
public void setServletRequest(HttpServletRequest request) {
this.req=request;
}
}

第十四讲 类型转换

  • Struts2中,对常用的数据类型已经自动进行了类型装换工作。但是对于自定义类型,Struts2没法去做类型转换工作。需要自定义类型转换器来实现类型的转换。
  • 类型转换的实现步骤:
    • 新建一个类型转换并继承StrutsTypeConverter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package cn.zhuchuli.pointconverter;

import java.util.Map;

import org.apache.struts2.util.StrutsTypeConverter;

import cn.zhuchuli.vo.Point;
/***
* 自定义类型转换器
* @author 1huangzewei
*
*/
public class PointConverter extends StrutsTypeConverter {
/**
* 将表单提交的数据在这个方法中被转换。
* 返回值是目标点对象
* Converts one or more String values to the specified class.
*
* @param context the action context
* @param values the String values to be converted, such as those submitted from an HTML form
* @param toClass the class to convert to
* @return the converted object
*/
@Override
public Object convertFromString(Map context, String[] values, Class toClass) {
System.out.println("将String转化为指定类型");
String value=values[0];
Point point=new Point();
point.setX(Integer.parseInt(value.substring(1,value.indexOf(","))));
point.setY(Integer.parseInt(value.substring(value.indexOf(",")+1,value.indexOf(")"))));
return point;
}
/**
* Converts the specified object to a String.
*
* @param context the action context
* @param o the object to be converted
* @return the converted String
* 使用ognl表达式获取值会调用该方法
*/
@Override
public String convertToString(Map context, Object o) {
// TODO Auto-generated method stub
System.out.println("将指定的类型转化为String");
Point p=(Point) o;
return "("+p.getX()+","+p.getY()+")";
}

}
  • src下添加xwork-conversion.properties配置文件
    • # be converted type=converter
    • cn.zhuchuli.vo.Point = cn.zhuchuli.pointconverter.PointConverter

第十五讲 数据校验一 (硬编码实现)

  • 数据校验分为2类,一类是前端的数据校验,一般通过js实现,一类是后端的数据验证。在对一些安全级别较高项目都需要对其进行后台验证。Struts2提供了两种后台校验方法,一种是硬编码实现,一种是校验框架实现。
  • 如果要使用struts2的数据校验功能,action需要继承ActionSupport类。在该类中提供了validate方法,可以将验证规则写在该方法中,只有方法执行通过之后,才会执行业务方法。
  • 实现步骤
    • 业务处理类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.zhuchuli.action;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.opensymphony.xwork2.ActionSupport;

import cn.zhuchuli.vo.User;

/***
* 数据校验
* @author 1huangzewei
*
*/
public class UserAction extends ActionSupport{
private User user;
//限制validate方法,方法通过执行后执行execute发明方法。
//默认该方法是通过的。
//在validate方法中,添加了FieldError或者ActionError那么该方法将执行不能通过,并且返回结果集为INPUT
//
@Override
public void validate() {
System.out.println("-------validate-------");
Pattern p=Pattern.compile("[\\w]+[A-Za-z0-9]{6,16}");
Matcher m=p.matcher(user.getName());
if(!m.find()) {
//用户名不合法,添加错误信息
this.addFieldError("user.name", "用户名不合法");
}
super.validate();
}
public String execute() {
System.out.println("------execute---------");
System.out.println(user);
return SUCCESS;
}
public User getUser() {
return user;
}

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

}
  • 在配置文件中配置input结果集
1
2
3
4
<action name="user" class="com.zhuchuli.action.UserAction">
<result>/success.jsp</result>
<result name="input">/save.jsp</result>
</action>
  • 在页面中添加struts2的标签库,并且添加错误标签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="user.action" method="post">
name:<input type="text" name="user.name"/><s:fielderror fieldName="user.name"></s:fielderror><br>
sex:<input type="text" name="user.sex"/><br>
age:<input type="text" name="user.age"/><br>
<input type="submit" value="登录"/>
</form>
</body>
</html>
  • 通过上述方法来实现数据校验会有一个问题。一个业务类中有多个业务方法,并且每个业务方法的验证规则可能不一致,但是所有的业务方法都会通过validate。导致功能不能实现。在struts2中,如果一个业务类中有多个业务方法,那么需要为每个业务方法添加自己的验证方法。验证方法的命名规则为validate+业务方法名(首字母大写)。这样执行业务方法时,执行顺序为validateXxxx ---> validate ---> xxxx。这样的话,validate方法中填写的公共的验证方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.zhuchuli.action;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.opensymphony.xwork2.ActionSupport;

import cn.zhuchuli.vo.User;

/***
* 数据校验
* @author 1huangzewei
*
*/
public class UserAction extends ActionSupport{
private User user;
//限制validate方法,方法通过执行后执行execute发明方法。
//默认该方法是通过的。
//在validate方法中,添加了FieldError或者ActionError那么该方法将执行不能通过,并且返回结果集为INPUT
//
@Override
public void validate() {
System.out.println("-------validate-------");
Pattern p=Pattern.compile("[\\w]+[A-Za-z0-9]{6,16}");
Matcher m=p.matcher(user.getName());
if(!m.find()) {
//用户名不合法,添加错误信息
this.addFieldError("user.name", "用户名不合法");
}
super.validate();
}
public void validateExecute() {
System.out.println("-------validateExecute-------");
Pattern p=Pattern.compile("[\\w]+[A-Za-z0-9]{6,16}");
Matcher m=p.matcher(user.getName());
if(!m.find()) {
//用户名不合法,添加错误信息
this.addFieldError("user.name", "用户名不合法");
}
super.validate();
}
public String execute() {
System.out.println("------execute---------");
System.out.println(user);
return SUCCESS;
}
public User getUser() {
return user;
}

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

}

第十五讲 数据校验二(使用Struts2提供的校验框架实现数据校验)

  • 实现步骤
    • 创建jsp页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="user.action" method="post">
name:<input type="text" name="user.name"/><s:fielderror fieldName="user.name"></s:fielderror><br>
sex:<input type="text" name="user.sex"/><br>
age:<input type="text" name="user.age"/><br>
<input type="submit" value="登录"/>
</form>
</body>
</html>
  • 创建Action
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.zhuchuli.action;




import com.opensymphony.xwork2.ActionSupport;

import cn.zhuchuli.vo.User;

public class UserFormAction extends ActionSupport{
private User user;

public String execute() {
System.out.println("------execute---------");
System.out.println(user);
return SUCCESS;
}
public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}
}
  • Action类所在包下,添加校验规则文件以ActionClassName-validation.xml命名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.dtd">
<validators>
<!--field表示对哪个表单域进行验证 -->
<field name="user.name">
<!-- 字段验证器 Struts2默认提供了很多验证器 -->
<field-validator type="requiredstring">
<message>你必须输入姓名。</message>
</field-validator>
<field-validator type="stringlength">
<param name="maxLength">10</param>
<param name="minLength">4</param>
<message>你必须输入姓名在${maxLength}和${minLength}之间</message>
</field-validator>
</field>
</validators>
  • 配置Struts.xml文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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" namespace="/" extends="struts-default">
<!-- <action name="point" class="com.zhuchuli.action.PointAction">
<result>/success.jsp</result>
</action>

<action name="user" class="com.zhuchuli.action.UserAction">
<result>/success.jsp</result>
<result name="input">/save.jsp</result>
</action> -->
<action name="user" class="com.zhuchuli.action.UserFormAction">
<result>/success.jsp</result>
<result name="input">/save.jsp</result>
</action>
</package>

</struts>

第十七讲 Struts2拦截器

  • struts2中的所有功能都是由拦截器来实现的。拦截器是Struts2的核心。拦截器和过滤器非常相似,过滤器过滤的是所有的请求,拦截器纸过滤action,并且在struts2中所有的功能都是可插拔的。在struts2中还可以自定义拦截器来实现一些struts2没有提供的功能。在struts2中拦截器的实现是通过代理来实现的。(AOP)在Struts2中拦截器都是单列的,所有的action共享相同的拦截器,所以在拦截器定义常量时要注意线程安全问题。
  • Struts2中提供很多拦截器来实现各种功能,在开发中可以根据不同的应用选择不同的拦截器来实现功能。Struts2提供了一系列默认的拦截器(拦截器栈)来实现功能。
  • 默认拦截器说明
    • alias:对于HTTP请求包含的参数设置别名
    • autowiring:将某些JavaBean实例自动绑定到其他Bean对应的属性中。有点类似Spring的自动绑定,在Spring部分会详细说明。
    • Chain:在Web项目开发中,以前使用Struts开发时候经常碰到两个Action互相传递参数或属性的情况。该拦截器就是让前一Action的参数可以在现有Action中使用。
    • conversionError:从ActionContext中将转化类型时候发生的错误添加到Action的值域错误中,在校验时候经常被使用到来显示类型转化错误的信息。
    • cookie:从Struts< xmlnamespace prefix="ST1" >2.0.7版本开始,可以把cookie注入Action中可设置的名字或值中。
    • createSession:自动创建一个HTTP的Session,尤其是对需要HTTPSession的拦截器特别有用。比如下面介绍的TokenInterceptor
    • debugging:用来对在视图间传递的数据进行调试。
    • ExecAndWait:不显式执行Action,在视图上显示给用户的是一个正在等待的页面,但是Action其实是在“背后”正在执行着。该拦截器尤其在对进度条进行开发的时候特别有用。
    • exception:将异常和Action返回的result相映射。
    • fileUpload:支持文件上传功能的拦截器。
    • i18n:支持国际化的拦截器。
    • logger:拥有日志功能的拦截器。
    • modelDrivenAction执行该拦截器时候,可以将getModel方法得到的result值放入值栈中
    • scopedModelDriven:执行该拦截器时候,它可以从一个scope范围检索和存储model值,通过调用setModel方法去设置model值。
    • params:将HTTP请求中包含的参数值设置到Action中。
    • prepare:假如Action继承了Preparable接口,则会调用prepare方法。
    • staticParams:对于在struts.xml文件中Action中设置的参数设置到对应的Action中。
    • scope:在session或者application范围中设置Action的状态
    • servletConfig:该拦截器提供访问包含HttpServletResquestHttpServletResponse对象的Map的方法。
    • timer:输出Action的执行时间。
    • token:避免重复提交的校验拦截器
    • tokenSession:和token拦截器类似,但它还能存储提交的数据到session里。
    • validation:运行在action-validation.xml(校验章节将介绍)文件中定义的校验规则。
    • workflow:在Action中调用validate校验方法。如果Action有错误则返回到input视图。
    • store:执行校验功能时候,该拦截器提供存储和检索Action的所有错误和正确信息的功能。
    • checkbox:视图中如果有checkbox存在的情况,该拦截器自动将unchecked的checkbox当作一个参数(通常值为“false”)记录下来。这样可以用一个隐藏的表单值来记录所有未提交的checkbox,而且缺省uncheckedcheckbox值是布尔类型的,如果视图中checkbox的值设置的不是布尔类型,它就会被覆盖成布尔类型的值。
    • profiling:通过参数来激活或不激活分析检测功能,前提是Web项目是在开发模式下。(涉及到调试和性能检验时使用)
    • roles:进行权限配置的拦截器,如果登录用户拥有相应权限才去执行某一特定Action。
  • Struts2中的默认拦截器是定义在struts-default.xml中。这些拦截器都是定义在struts-default包下的。所以在使用struts2时定义的package要直接或间接继承struts-default
  • 常见内置拦截器的使用,使用内置拦截器是需要引用拦截器。
    • Timer拦截器的使用
      • 创建action
1
2
3
4
5
6
7
8
package com.zhuchuli.interceptor;

public class TimerAction {
public String execute() {
System.out.println("Hello action");
return "none";
}
}
  • 在配置文件中进行配置
1
2
3
4
5
6
7
8
9
10
11
<?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" namespace="/" extends="struts-default">
<action name="interceptor" class="com.zhuchuli.interceptor.TimerAction">
<result></result>
<!-- 使用拦截器时,需要在指定的action中引用拦截器 -->
<interceptor-ref name="timer"></interceptor-ref>
</action>
</package>
</struts>
  • token拦截器的使用
    • 创建Action
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.zhuchuli.interceptor;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;
import com.zhuchuuli.vo.User;

public class UserAction extends ActionSupport{
private User user;
public String toSave() {
return Action.SUCCESS;
}
public String save() {
System.out.println("add user to database");
System.out.println(user);
return Action.SUCCESS;
}
public User getUser() {
return user;
}

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

}
  • 在页面中添加token标签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>提交页面</title>
</head>
<body>
<form action="interceptor.action" method="post">
<s:token></s:token>
name:<input type="text" name="user.name"/><br>
age:<input type="text" name="user.age"/><br>
<input type="submit" value="save" />
</form>
</body>
</html>
  • action中引用token
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<action name="save" class="com.zhuchuli.interceptor.UserAction" method="save">

<interceptor-ref name="token"></interceptor-ref>
<interceptor-ref name="basicStack"/>
<result>/success.jsp</result>
<result name="invalid.token">/invalid.jsp</result>
<!-- 使用拦截器时,需要在指定的action中引用拦截器 -->
<!-- <interceptor-ref name="timer"></interceptor-ref> -->
<!-- 当用户重复提交时,会返回invalid.token结果集 -->

</action>
<action name="toSave" class="com.zhuchuli.interceptor.UserAction" method="toSave">
<result>/save.jsp</result>
</action>
  • 编写失效页面。
1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>表单不能重复提交</h1>
</body>
</html>

第十八讲 自定义拦截器

  • 在开发中,经常会使用到struts2中没有提供的一些功能,这时需要我们自定义拦截器来实现。当引用自定义拦截器后,struts2提供的默认拦截器将不起作用,需要重新引入。
  • 自定义拦截器的实现步骤:
    • 定义拦截器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.zhuchuli.myinterceptor;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;

/***
* 自定义拦截器
* 有两种实现方式
* 实现Interceptor接口
* 继承AbstractInterceptor类
* @author 1huangzewei
*
*/
public class MyInterceptor implements Interceptor{

@Override
public void destroy() {
// TODO Auto-generated method stub

}

@Override
public void init() {
// TODO Auto-generated method stub

}
//拦截器的主体实现
/***
* 当拦截器的方法被调用执行后,需要调用 invocation.invoke调用下一个拦截器。
* 如果没有拦截器,那么执行action中的业务方法。
*/
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("-------------------自定义拦截器被执行了o-------------------");
//返回值为结果集
return invocation.invoke();
}

}
  • package中配置拦截器
1
2
3
4
<!-- 拦截器的配置 -->
<interceptors>
<interceptor name="MyIntercept" class="com.zhuchuli.myinterceptor.MyInterceptor"></interceptor>
</interceptors>
  • 在使用action中引用拦截器
1
2
3
4
5
6
<action name="timer" class="com.zhuchuli.interceptor.TimerAction">
<interceptor-ref name="timer"></interceptor-ref>
<!-- 使用自定义拦截器 -->
<interceptor-ref name="MyIntercept"></interceptor-ref>
<result>/success.jsp</result>
</action>
  • 登录拦截器的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.zhuchuli.myinterceptor;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
/***
* 登录拦截器的实现
* @author 1huangzewei
*
*/
public class LoginInterceptor implements Interceptor{

@Override
public void destroy() {
// TODO Auto-generated method stub

}

@Override
public void init() {
// TODO Auto-generated method stub

}
/**
* 判断是否是登录的action,如果是,继续执行
* 如果不是 判断session中是否存在用户,
* 如果没有,跳转到登录界面
* 如果有继续执行
*/
@Override
public String intercept(ActionInvocation invocation) throws Exception {
String actionName= invocation.getProxy().getActionName();
if("login".equals(actionName)) {
return invocation.invoke();
}
Object currentUser=invocation.getInvocationContext().getSession().get("currentUser");

if(currentUser!=null) {
return invocation.invoke();
}
return Action.LOGIN;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<package name="default" namespace="/" extends="struts-default">
<!-- 拦截器的配置 -->
<interceptors>
<interceptor name="MyIntercept" class="com.zhuchuli.myinterceptor.MyInterceptor"></interceptor>
<interceptor name="loginIntercept" class="com.zhuchuli.myinterceptor.LoginInterceptor"></interceptor>
</interceptors>
<!-- 配置全局结果集 -->
<!-- 当前action结果集找不到,就去找全局结果集,如果再找不到,返回404 -->
<global-results>
<result name="login">/login.jsp</result>
<result name="input">/login.jsp</result>
</global-results>
<action name="timer" class="com.zhuchuli.interceptor.TimerAction">
<interceptor-ref name="timer"></interceptor-ref>
<!-- 使用自定义拦截器 -->
<interceptor-ref name="MyIntercept"></interceptor-ref>
<result>/success.jsp</result>
<interceptor-ref name="loginIntercept"/>
</action>
<action name="save" class="com.zhuchuli.interceptor.UserAction" method="save">
<!-- 使用拦截器时,需要在指定的action中引用拦截器 -->
<!-- <interceptor-ref name="timer"></interceptor-ref> -->
<!-- 当用户重复提交时,会返回invalid.token结果集 -->
<interceptor-ref name="token"></interceptor-ref>
<interceptor-ref name="basicStack"/>
<interceptor-ref name="loginIntercept"/>
<result>/success.jsp</result>
<result name="invalid.token">/invalid.jsp</result>
</action>
<action name="toSave" class="com.zhuchuli.interceptor.UserAction" method="toSave">
<result>/save.jsp</result>
<interceptor-ref name="loginIntercept"/>
</action>
<action name="login" class="com.zhuchuli.interceptor.UserAction02" method="login">
<result>/success.jsp</result>
<interceptor-ref name="loginIntercept"/>
<interceptor-ref name="basicStack"/>
</action>
</package>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.zhuchuli.interceptor;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;
import com.zhuchuuli.vo.User02;

public class UserAction02 extends ActionSupport{
private User02 user;
//进行数据校验
public void validate() {
Pattern p=Pattern.compile("[\\w]+[A-Za-z0-9]{6,16}");
Matcher m=p.matcher(user.getPwd());
if(!((user.getName().length()>=4||user.getName().length()<=10)&& m.find())) {
//用户名不合法,添加错误信息
this.addFieldError("user.name", "用户名或密码不合法");
}
}
public String toSave() {

return Action.SUCCESS;
}
public String login() {
System.out.println("------login---------");
//ServletActionContext.getRequest().getSession().setAttribute("currentUser", user);
//采用解耦的方式进行SevletAPI操作
if("possible".equals(user.getName())&&"123456".equals(user.getPwd())) {
ServletActionContext.getContext().getSession().put("currentUser", user);
System.out.println(ServletActionContext.getContext().getSession().get("currentUser"));
return Action.SUCCESS;
}else {
return Action.LOGIN;
}
}
public User02 getUser() {
return user;
}

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

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.zhuchuuli.vo;

public class User02 {
private String name;
private String pwd;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

<form action="login.action" method="post">
用户名:<input type="text" name="user.name"/><br>
密码:<input type="password" name="user.pwd"/><br>

<input type="submit" value="登录"/>
</form>
</body>
</html>

第十九讲 拦截器栈和方法拦截器


  • 拦截器栈:就是一组拦截器,放在一个配置中,在拦截器栈中可以引用多个拦截器,在真正需要调用时只需要一个栈即可,方便引用,拦截器中可以引用另外一个拦截器栈,
1
2
3
4
5
6
7
8
9
10
11
<!-- 拦截器的配置 -->
<interceptors>
<interceptor name="MyIntercept" class="com.zhuchuli.myinterceptor.MyInterceptor"></interceptor>
<interceptor name="loginIntercept" class="com.zhuchuli.myinterceptor.LoginInterceptor"></interceptor>
<!-- 拦截器栈 -->
<interceptor-stack name="myStack">
<interceptor-ref name="basicStack"/>
<interceptor-ref name="loginIntercept"/>
<interceptor-ref name="basicStack"/>
</interceptor-stack>
</interceptors>
  • 拦截器栈的引用,与拦截器引用一致,可以将拦截器栈当做拦截器来使用
1
<interceptor-ref name="myStack"></interceptor-ref>
  • 设置默认拦截器栈
1
2
<!-- 设置默认拦截器-->
<!-- <default-action-ref name="myStack"></default-action-ref>-->
  • Struts2定义了默认的拦截器栈是defaultStack,里面有默认的18个拦截器。

  • 拦截器拦截的是整个action,action中的所有业务方法都会被拦截。比较粗粒度。有时只需拦截action中某个方法或某几个方法就能实现功能,那么拦截器就会造成效率降低。这时可以通过方法拦截器来解决这个问题。
  • 方法拦截器的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.zhuchuli.myinterceptor;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;

/***
* 方法拦截器的实现 继承 MethodFilterInterceptor
* @author 1huangzewei
*
*/
public class MethodInterceptor extends MethodFilterInterceptor{

/**
*
*/
private static final long serialVersionUID = 1L;

@Override
protected String doIntercept(ActionInvocation invocation) throws Exception {
System.out.println("方法拦截器被执行");
return invocation.invoke();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<interceptors>
<interceptor name="MyIntercept" class="com.zhuchuli.myinterceptor.MyInterceptor"></interceptor>
<interceptor name="loginIntercept" class="com.zhuchuli.myinterceptor.LoginInterceptor"></interceptor>
<interceptor name="methodInterceptor" class="com.zhuchuli.myinterceptor.MethodInterceptor"></interceptor> <!-- 拦截器栈 -->
<interceptor-stack name="myStack">
<interceptor-ref name="basicStack"/>
<interceptor-ref name="loginIntercept"/>
<interceptor-ref name="basicStack"/>
<interceptor-ref name="methodInterceptor">
<!-- 配置哪些方法被拦截 -->
<param name="includeMethods">timer,toSave</param>
<!-- 配置哪些方法不被拦截 -->
<param name="excludeMethods">timer,toSave</param>
</interceptor-ref>
</interceptor-stack>
</interceptors>

第二十讲 内置拦截器的使用情况。

第二十一讲 struts 文件上传(使用默认的内置拦截器)

  • 在大部分应用中都有文件上传的功能。servlet中可以使用第三方插件来实现文件上传:smartfileuploadcommons-fileuoload
  • 文件上传步骤
    • 添加jsp页面,注意表单提交必须是post提交,并且设置`enctype=”multipart/form-data”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<form action="upload.action" method="post" enctype="multipart/form-data">
文件:<input type="file" name="file"/><input type="submit" value="上传">
</form>
</body>
</html>
  • 添加文件上传处理类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.zhuchuli.action;

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileUtils;
import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.Action;

/***
* 文件上传处理类
* @author 1huangzewei
*
*/
public class UploadAction {
//File属性名与表单域相同 类型为File
private File file;
//上传文件的名称,也是由struts2设置好
//属性名=表单域名+FileName
private String fileFileName;
//文件类型 = 表单域名+ContentType
private String fileContentType;

//上传文件的业务方法
public String upload() {
//获取上传文件目录
String path=ServletActionContext.getServletContext().getRealPath("/upload");
try {
FileUtils.copyFile(file, new File(path,fileFileName));
return Action.SUCCESS;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return Action.ERROR;
}

public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
public String getFileFileName() {
return fileFileName;
}
public void setFileFileName(String fileFileName) {
this.fileFileName = fileFileName;
}
public String getFileContentType() {
return fileContentType;
}
public void setFileContentType(String fileContentType) {
this.fileContentType = fileContentType;
}



}
  • 添加配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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 name="struts.multipart.saveDir" value="d:/"></constant>
<!-- 常量设置 --><!-- 设置上传文件总大小 -->
<constant name="struts.multipart.maxSize" value="67388135"></constant>
<package name="default" namespace="/" extends="struts-default">
<action name="upload" class="com.zhuchuli.action.UploadAction" method="upload">
<result>/success.jsp</result>
<result name="error">/error.jsp</result>
<interceptor-ref name="fileupload">
<!-- 设置上传单个文件的大小 -->
<param name="maximumSize">67388135</param>
</interceptor-ref>
<interceptor-ref name="basicStack"></interceptor-ref>
</action>
</package>
</struts>

第二十二讲 批量文件上传

  • 批量文件下载
    • Jsp页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<form action="batch.action" method="post" enctype="multipart/form-data">
文件1:<input type="file" name="file"/><br>文件2:<input type="file" name="file"/><br><input type="submit" value="上传">
</form>
</body>
</html>
>>* 批量下载业务处理方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.zhuchuli.action;

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileUtils;
import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;

/***
* 文件上传处理类
* @author 1huangzewei
*
*/
public class BatchUploadAction extends ActionSupport{

/**
*
*/
private static final long serialVersionUID = 1L;
//File属性名与表单域相同 类型为File
private File file[];
//上传文件的名称,也是由struts2设置好
//属性名=表单域名+FileName
private String fileFileName[];
//文件类型 = 表单域名+ContentType
private String fileContentType[];

//上传文件的业务方法
public String upload() {
//System.out.println(file+";"+fileFileName);
//获取上传文件目录
String path=ServletActionContext.getServletContext().getRealPath("/upload");
try {
for(var i=0;i<file.length;i++) {
FileUtils.copyFile(file[i], new File(path,fileFileName[i]));
}
return Action.SUCCESS;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return Action.ERROR;
}

public File[] getFile() {
return file;
}

public void setFile(File[] file) {
this.file = file;
}

public String[] getFileFileName() {
return fileFileName;
}

public void setFileFileName(String[] fileFileName) {
this.fileFileName = fileFileName;
}

public String[] getFileContentType() {
return fileContentType;
}

public void setFileContentType(String[] fileContentType) {
this.fileContentType = fileContentType;
}
}
  • struts.xml配置文件
1
2
3
4
5
6
7
8
<action name="batch" class="com.zhuchuli.action.BatchUploadAction" method="upload">
<result>/success.jsp</result>
<result name="error">/error.jsp</result>
<interceptor-ref name="fileUpload">
<param name="maximumSize">67388135</param>
</interceptor-ref>
<interceptor-ref name="basicStack"></interceptor-ref>
</action>

第二十三讲 文件下载

  • 文件下载可以通过超链接直接实现,但是通过超链接直接下载的文件不安全。任何用户得到超链接都可以下载,没法权限的控制。浏览器如果能打开文件,那么浏览会直接将文件打开。
  • 通过流的方式来实现文件下载
    • 业务处理方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.zhuchuli.action;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.Action;
/***
* 实现文件下载
* @author 1huangzewei
*高等数学 水以v立方厘米/秒的输的灌入圆锥形水箱.水箱尖点朝下
*/
public class DownloadAction {
private String fileName;

public String execute() {
System.out.println("Download");
return Action.SUCCESS;
}
public InputStream getInputStream() {
String path=ServletActionContext.getServletContext().getRealPath("/download");
try {
return new FileInputStream(new File(path,fileName));
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
}
}
public String getFileName() {
return fileName;
}

public void setFileName(String fileName) {
this.fileName = fileName;
}


}
  • 配置文件
1
2
3
4
5
<action name="download" class="com.zhuchuli.action.DownloadAction">
<result type="stream">
<param name="contentDisposition">attachment;filename=${fileName}</param>
</result>
</action>
  • jsp页面
1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Struts2文件下载</title>
</head>
<body>
<a href="download.action?fileName=jiaoben5614.rar">文件下载</a>
</body>
</html>

第二十四讲 ajax

  • Struts2中依然可以使用Servlet中的ajax
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.zhuchuli.ajax;

import java.io.IOException;

import javax.servlet.http.HttpServletResponse;

import org.apache.struts2.ServletActionContext;

public class AjaxAction {
private String username;

//ajax
public String checkName() throws IOException {
HttpServletResponse resp=ServletActionContext.getResponse();
System.out.println("checkName --->" + username);
if("possible".equals(username)) {
resp.getWriter().print("true");
}else {
resp.getWriter().print("false");
}
return "none";
}
public String getUsername() {
return username;
}

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

}
  • Struts2对ajax也提供了异步支持,使用步骤
    • 导入相关jar包
    • 编写jsp页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ajax</title>
<script type="text/javascript" src="js/jquery-3.2.1.js"></script>
<script type="text/javascript">
$(function(){
$("#btn").click(function(){
$.post("list.action",function(data){
console.log(data);
var html="";
for(var i=0;i<data.length;i++){
html+="<tr><td>"+data[i].id+"</td><td>"+data[i].name+"</td><td>"+data[i].age+"</td></tr>";
}
$("#content").html(html);
},'json')
})
})
</script>
</head>
<body>
<button id="btn">获取数据</button>
<table width="80%" align="center">
<thead>
<tr>
<td>编号</td>
<td>姓名</td>
<td>年龄</td>
</tr>
</thead>
<tbody id="content">

</tbody>
<tfoot></tfoot>
</table>
</body>
</html>
  • 编写业务处理方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.zhuchuli.ajax;

import java.util.ArrayList;
import java.util.List;

import com.opensymphony.xwork2.Action;
import com.zhuchuli.vo.User;

public class JsonAction {
private List<User> list;
//获取数据
public String list() {
list=new ArrayList<User>();
list.add(new User(1,"张三",19));
list.add(new User(2,"李四",20));
list.add(new User(3,"possible",16));
for(User u:list) {
System.out.println(u.getId()+"-->"+u.getName()+"-->"+u.getAge());
}
return Action.SUCCESS;

}
public List<User> getList() {
return list;
}
public void setList(List<User> list) {
this.list = list;
}

}
  • 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
<?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" namespace="/" extends="json-default">
<action name="checkName" class="com.zhuchuli.ajax.AjaxAction" method="checkName"></action>
<action name="hello" class="com.zhuchuli.ajax.HelloAction"></action>
<action name="list" class="com.zhuchuli.ajax.JsonAction" method="list">
<result type="json">
<param name="root">list</param>
</result>
</action>
</package>
</struts>

第二十五讲 异常处理

  • 异常处理在实际的应用开发中必不可少。比如:开发中不能直接将错误信息展示给用户看,需要对其进行处理,给用户一个更友好的提示。
    • 异常处理类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.zhuchuli.action;

import com.opensymphony.xwork2.Action;
import com.zhuchuli.exception.UserException;
import com.zhuchuli.service.UserServiceImpl;
import com.zhuchuli.service.Userserivce;

public class UserAction {
private Userserivce userService=new UserServiceImpl();
//try-catch 需要通过异步进行处理ss
public String delete() throws UserException {
userService.delete(0);
return Action.SUCCESS;
}
}
  • 配置文件
1
2
3
4
5
<action name="delete" class="com.zhuchuli.action.UserAction" method="delete">
<result>/success.jsp</result>
<result name="userexp">/exp.jsp</result>
<exception-mapping result="userexp" exception="com.zhuchuli.exception.UserException"></exception-mapping>
</action>

第二十六讲 Struts最新版