Struts

一、Struts2 概述

  • 应用于 JavaEE 三层结构中 web 层框架
  • Struts2 框架是在 Struts1WebWork 基础之上发展的全新框架
  • 有一个过滤器,根据不同的操作,在类里面写不同的方法

1.1 - 简单案例

1.1.1 - 引入 Jar 包

  • asm-7.1.jar
  • asm-commons-7.1.jar
  • asm-tree-7.1.jar
  • commons-fileupload-1.4.jar
  • commons-io-2.6.jar
  • commons-lang3-3.8.1.jar
  • freemarker-2.3.28.jar
  • javassist-3.20.0-GA.jar
  • log4j-api-2.12.1.jar
  • log4j-core-2.12.1.jar
  • ognl-3.1.26.jar
  • struts2-core-2.5.22.jar

1.1.2 - 新建 struts.xml

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">

- struts.xml 文件位置固定在 src 文件夹

1.1.3 - 配置 action

1
2
3
4
5
6
7
8
9
10
11
12
<struts>
<package name="···" extends="struts-default" namespace="/···">
<action name="···" class="Java 文件全路径">
<result name="ok"></result>
</action>
</package>
</struts>

- 在 struts.xml 文件中进行
- 访问的基本路径相同的 action 放在同一个 package 中
- package 的 extends 属性是固定的 struts-default
- 外部的访问路径由 package 的 namespace 和 action 的 name 构成

1.1.4 - 配置 filter

1
2
3
4
5
6
7
8
9
10
11
12
  <filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

- 在 web.xml 文件中进行
- filter 中的类容固定
- filter-mapping 中配置要用到的 filter 和 要处理的路径

1.1.5 - 书写 Java 文件

Java 文件要重写以下方法

1
2
3
public 返回类型 execute() {

}

1.2 - 过滤器

  • 外来请求首先会被汇总到过滤器

  • 过滤器在服务器启动时创建,执行 init 方法

  • 过滤器要做的事

    • 过滤器会获取请求路径

    • 截取请求路径中的有效字符串

    • src 下面找到 struts.xml 使用 dom4j 进行解析

    • 用截取到的字符串与 struts.xml 中的 action 进行匹配

    • 如果匹配到对应的 action 则根据其中的全路径,反射创建对应 Java

      • 反射:

      • Class clazz = Class.forName("全路径");
        // 反射创建类
        Method m = clazz.getMethod("execute");
        // 获取方法
        Object obj = m.invoke();
        // 方法执行并获取返回值
        
        1

    1

  • 将返回值与该 action 中的 result 进行匹配

    • 如果匹配成功,则跳转到相应页面
      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
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115

      ### 1.2.1 - init()

      加载自己创建的配置文件和 `struts2` 自带配置文件

      ### 1.2.2 - doFilter()

      **request = warpRequest(request);**

      增强

      ## 1.3 - struts.xml

      ### 1.3.1 - <package >

      类似于代码包,用于区分不同的 `action` ,必须首先写 `package` 标签,在 `package` 标签中才能配置 `action`,在一个 `package` 中可以有多个 `action`

      #### - name

      和功能本身没有任何关系,只要重复,可以随便取

      #### - extends

      属性值是固定的,写了这个属性之后,在 `package` 中配置的 `action` 才具有 `action` 该有的功能

      #### - namespace

      要和 `action` 标签的 `name` 构成访问路径,默认值是 `/`

      ### 1.3.2 - <action >

      配置 `action` 的访问路径及 `Java` 文件的绑定

      #### - name

      要和 `package` 标签的 `name` 构成访问路径

      可以用通配符 ***** 进行匹配

      #### - class

      `action` 所绑定 `Java` 文件的全路径

      #### - method

      指定执行 `Java` 文件中的哪一个方法

      在通配符模式下,用 `{1}` 来获取方法,获取到的方法名即 ***** 所代替的字符串

      #### - action 的另外两种编写方式

      - `implements Action`
      - `extends ActionSupport`

      ### 1.3.3 - <result >

      根据 `action` 方法返回值,配置到不同的路径

      #### - name

      和 `action` 返回值一样

      #### - type

      指定如何到达配置路径的方法

      - `dispatcher` 请求转发,默认
      - `redirect` 重定向
      - `chain` 转发到 `action`
      - `redirectAction` 重定向到 `Action`

      ### 1.3.4 - <constant >

      #### - name

      #### - value

      另外两种常量修改方式

      - 在 `src` 下创建 `struts.properties`
      - 在 `web.xml` 配置

      ### 1.3.5 - <include >

      #### - file

      文件路径

      ### 1.3.6 - <global-results >

      当有多个 `action` 返回值相同、且其 `result` 返回的内容也相同,则可以使用这个

      # 二、数据处理

      ## 2.1 - 使用 ActionContext 类

      - 需要创建 `ActionContext` 对象
      - 不是 `new` 出来的

      ## 2.2 - 使用 ServletActionContext 类



      ## 2.3 - 使用 Interface 注入

      ```java
      public class A implements ServletRequestAware {

      private HttpServletRequest request;

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

      }

三、数据封装

3.1 - 属性驱动

  • 直接把表单提交属性封装到 action 属性
  • 实现步骤
    • action 成员变量位置定义变量 – 变量名和表单输入项的 name 属性值一样
    • 生成变量的 settergetter
  • 使用属性封装获取表单数据到属性里面,不能把数据直接封装到实体类

3.2 - 模型驱动

  • 使用模型驱动方式,可以直接把表单数据封装到实体类对象里面

  • 实现步骤

    • action 实现接口 ModelDriven

    • 实现接口里的方法 getModel

    • action 里创建实体类对象

    • ```java
      public class A extends ActionSupport implements ModelDriven <类型> {

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11

      private 类型 a = new 类型();

      public 类型 getModel() {

      return a;

      }

      }

  • 属性驱动和模型封装不能同时使用,如果同时使用,只会执行模型封装

    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

    ## 3.3 - 表达式封装

    - 实现过程
    - 使用表达式封装可以把表单数据封装到实体类对象里面
    - 在 `action` 里面声明实体类
    - 生成实体类的 `setter` 和 `getter` 方法

    # 四、Ognl 概述

    - `EL` 表达式:在 `JSP` 中获取域对象里面的值
    - `Ognl` 表达式:在 `Struts2` 实现值栈数据
    - 一般把 `Ognl` 在 `Struts2` 里操作,和 `Struts2` 标签一起使用操作值栈
    - 不是 `Struts2` 的一部分,只是经常和 `Struts2` 一起使用

    ## 4.1 - 获取值栈对象

    ### 4.1.1 - 常用方式

    使用 `ActionContext` 类里面的方法得到值栈对象

    - 获取 `ActionContext` 对象
    - `ActionContext context = ActionContext.getContext();`
    - 调用方法得到值栈对象
    - `ValueStack stack = context.getValueStack();`

    ## 4.2 - 值栈内部结构

    ### 4.2.1 - 第一部分:root

    - 结构是 `List` 集合
    - 一般操作的都是 `root` 里的数据

    ### 4.2.2 - 第二部分: context

    - 结构是 `Map` 集合
    - 很少操作
    - 几个键值对
    - `request`:`request` 引用
    - `session`:`HttpSession` 引用
    - `application`: `ServletContext` 引用
    - `parameters`:传递相关参数
    - `attr`: 获取域对象

    ## 4.3 - 向值栈放数据

    **调用 set()**

    ```java
    ActionContext context = ActionContext.getContext();

    ValueStack stack = context.getValueStack();

    stack.set("key", "value");

调用 push()

1
2
3
4
5
ActionContext context = ActionContext.getContext();

ValueStack stack = context.getValueStack();

stack.put("");

生成 get()

1
2
3
4
5
private String name;

public String getName() {
return this.name;
}

4.3.1 - 向值栈放对象

  • 定义对象变量
1
private User user = new User();
  • 生成变量的 get 方法
1
2
3
public User getUser() {
return user;
}
  • 在执行的方法里面向对象中设置值

4.3.2 - 向值栈放 List 对象

  • 定义 List 集合
  • 生成变量的 getter
  • 在执行的方法里面向 List 集合设置值

4.4 - 从值栈获取数据

  • 使用 Struts2 的标签 + Ognl 表达式获取值栈数据
    • <s:property value="Ognl 表达式"/>

4.4.1 - 获取字符串

action

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class A extends ActionSupport {

private String 变量名;

public String get变量名() {

return 变量名;

}

public String execute() throws Exception {

变量名 = "";
return SUCCESS;

}

}

struts.xml

1
2
3
4
5
6
<action name="自定义名称" class="action 全路径">

<result name="success">/返回的 jsp</result>

</action>

jsp

1
2
<s:property value="变量名"/>

4.4.2 - 获取对象

action

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class A extends ActionSupport {

private 类名 对象;

public String get对象() {

return 对象;

}

public String execute() throws Exception {

对象.属性 = "···";
···
对象.属性 = "···";
return SUCCESS;

}

}

struts.xml

1
2
3
4
5
6
<action name="自定义名称" class="action 全路径">

<result name="success">/返回的 jsp</result>

</action>

jsp

1
2
3
4
<s:property value="对象.属性"/>
···
<s:property value="对象.属性"/>

4.4.3 - 获取 List 对象

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
public class A extends ActionSupport{

List<类型> list = new List();

public String execute() {

类型 对象1 = new 类型();
对象1.属性 = "···";
···
对象1.属性 = "···";

类型 对象2 = new 类型();
对象2.属性 = "···";
···
对象2.属性 = "···";

list.add(对象1);
list.add(对象2);

return SUCCESS;

}

}

struts.xml

1
2
3
4
5
6
<action name="自定义名称" class="action 全路径">

<result name="success">/返回的 jsp</result>

</action>

jsp

第一种方式

1
2
3
4
5
6
7
8
<s:property value="list[0].属性"/>
···
<s:property value="list[0].属性"/>

<s:property value="list[1].属性"/>
···
<s:property value="list[1].属性"/>

第二种方式

1
2
3
4
5
6
<s:iterator value="list">
<s:property value="属性"/>
···
<s:property value="属性"/>
</s:iterator>

第三种方式

1
2
3
4
5
6
7
8
<s:iterator value="list" var="自定义名称">
<s:property value="#自定义名称.属性"/>
···
<s:property value="#自定义名称.属性"/>
</s:iterator>

- var 会被临时放入 context,需要使用特殊符号 `#` 才能取到

4.4.4 - 其他操作

- 获取 setter 方法存放的数据

1
2
<s:property value="栈的key"/>

- 获取 push 方法存放的数据

1
2
3
4
<s:property value="[下标].top"/>

- 因为 push 进栈的没有名字,默认存放在 top 数组

4.5 - 两种符号的使用

4.5.1 -

先在 action 向域中添加对象

1
2
3
4
5
6
7
8
public String execute() throws Exception {

HttpServletRequest request = ServletActionContext.getRequest();
request.setAttribute(key, value);
return SUCCESS;

}

struts.xml

1
2
3
4
5
6
<action name="自定义名称" class="action 全路径">

<result name="success">/返回的 jsp</result>

</action>

jsp

1
2
<s:property value="#context域的key.域对象名称"/>

4.5.2 - %

struts2 标签里使用 Ognl 表达式,如果直接在 struts2 表单标签里面使用 Ognl 表达式不识别,只有 % 之后才会识别

1
2
<s:textfield name="" value=%{#context域的key.域对象名称}></s:textfield>

五、拦截器

5.1 - 概述

  • struts2 是框架,封装了很多功能,struts2 里面封装的功能都是在拦截器里面
  • struts2 里面封装了很多的功能,又很多拦截器,不是每次这些拦截器都执行,每次执行默认的拦截器
  • 默认拦截器位置:struts2-core-version.jar/struts-default.xml
  • action 创建之后,执行之前调用

5.2 - 底层原理

两大思想

  • Aop 思想
  • 职责链设计模式
    • 要执行多个操作,有添加、修改、删除
    • 首先执行添加操作,添加之后执行修改操作,最后执行删除操作

通过 AOP 将拦截器加入到执行过程

5.3 - 重要的概念

5.3.1 - 过滤器和拦截器

  • 过滤器:理论上可以拦截任意内容
  • 拦截器:只可以拦截 action

5.3.2 - Servlet 和 action

  • servlet 默认第一次访问时创建,创建一次,单实例对象
  • action 每次访问时创建,创建多次,多实例对象

5.4 - 自定义拦截器

5.4.1 - 继承类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A extends AbstractInterceptor {

}
// 而
class AbstractInterceptor implements Interceptor {

}

- 工作中推荐下面
class A extends MethodFilterInterceptor {

protected String doIntercept(ActionInvocation invocation) throws Exception {

}

}

5.4.2 - 重写方法

1
2
3
4
void init();
void destroy();
String intercept(ActionInvocation invocation);

5.4.3 - 建立关系

  • 在要拦截的 action 标签所在的 package 标签里面声明拦截器
1
2
3
4
<interceptors>
<interceptor name="自定义name" class="拦截器java文件全路径"/>
</interceptors>

  • 在具体的 action 标签里面使用声明的拦截器
1
2
<interceptor-ref name="上面的自定义name"/>

  • 如果在 struts.xml 中使用自定义的,则默认拦截器不会执行

    • 解决方法:手动引入默认拦截器
    1
    2
    <interceptor-ref name="paramsPrepareParamsStack"/>

  • 自定义拦截器会对 action 里所有方法都拦截

5.4.4 - 放不拦截某些方法

1
2
3
4
<interceptor-ref name="上面的自定义name">
<param name="excludeMethods">写方法名,多个用逗号隔开</param>
</interceptor-ref>