详解在Java的Struts2框架中配置Action的方法

时间:2021-08-05 18:03:08

在Struts2中Action部分,也就是Controller层采用了低侵入的方式。为什么这么说?这是因为在Struts2中action类并不需要继承任何的基类,或实现任何的接口,更没有与Servlet的API直接耦合。它通常更像一个普通的POJO(通常应该包含一个无参数的execute方法),而且可以在内容定义一系列的方法(无参方法),并可以通过配置的方式,把每一个方法都当作一个独立的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 example;
 
public class UserAction {
 
    private String username;
 
    private String password;
 
  public String execute() throws Exception {
 
       //…………..
 
    return “success”;
 
  }
 
  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;
 
  }
 
}

action访问servlet

在这个Action类里的属性,既可以封装参数,又可以封装处理结果。系统并不会严格区分它们。

但是为了使用户开发的Action类更规范,Struts2为我们提供了一个接口Action,该类定义如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
publicinterface Action {
 
  publicstaticfinal String ERROR="error";
 
  publicstaticfinal String INPUT="input";
 
  publicstaticfinal String NONE="none";
 
  publicstaticfinal String LOGIN="login";
 
  publicstaticfinal String SUCCESS="success";
 
  public String execute()throws Exception;
 
}

但是我们写Action通常不会实现该接口,而是继承该接口的实现类ActionSupport.

该类代码如下:

?
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
public class ActionSupport implements Action, Validateable, ValidationAware, TextProvider, LocaleProvider, Serializable {
 
   ................
 
  public void setActionErrors(Collection errorMessages) {
 
    validationAware.setActionErrors(errorMessages);
 
  }
 
  public Collection getActionErrors() {
 
    return validationAware.getActionErrors();
 
  }
 
  public void setActionMessages(Collection messages) {
 
    validationAware.setActionMessages(messages);
 
  }
 
  public Collection getActionMessages() {
 
    return validationAware.getActionMessages();
 
  }
 
    public Collection getErrorMessages() {
 
    return getActionErrors();
 
  }
 
    public Map getErrors() {
 
    return getFieldErrors();
 
  }
 
//设置表单域校验错误
 
  public void setFieldErrors(Map errorMap) {
 
    validationAware.setFieldErrors(errorMap);
 
  }
 
  public Map getFieldErrors() {
 
    return validationAware.getFieldErrors();
 
  }
 
  public Locale getLocale() {
 
    ActionContext ctx = ActionContext.getContext();
 
    if (ctx != null) {
 
      return ctx.getLocale();
 
    } else {
 
      LOG.debug("Action context not initialized");
 
      return null;
 
    }
 
  }
 
//获取国际化信息的方法
 
  public String getText(String aTextName) {
 
    return textProvider.getText(aTextName);
 
  }
 
  public String getText(String aTextName, String defaultValue) {
 
    return textProvider.getText(aTextName, defaultValue);
 
  }
 
  public String getText(String aTextName, String defaultValue, String obj) {
 
    return textProvider.getText(aTextName, defaultValue, obj);
 
  }
 
    .........
 
//用于访问国际化资源包的方法
 
  public ResourceBundle getTexts() {
 
    return textProvider.getTexts();
 
  }
 
  public ResourceBundle getTexts(String aBundleName) {
 
    return textProvider.getTexts(aBundleName);
 
  }
 
//添加action的错误信息
 
  public void addActionError(String anErrorMessage) {
 
    validationAware.addActionError(anErrorMessage);
 
  }
 
//添加action的普通信息
 
  public void addActionMessage(String aMessage) {
 
    validationAware.addActionMessage(aMessage);
 
  }
 
  public void addFieldError(String fieldName, String errorMessage) {
 
    validationAware.addFieldError(fieldName, errorMessage);
 
  }
 
   
 
  public void validate() {
 
  }
 
  public Object clone() throws CloneNotSupportedException {
 
    return super.clone();
 
  }
 
..........
 
}

前面说到struts2并没有直接与Servlet的API耦合,那么它是怎么访问Servlet的API的呢?

原来struts2中提供了一个ActionContext类,该类模拟了Servlet的API。其主要方法如下:

1)Object get (Object key):该方法模拟了HttpServletRequest.getAttribute(String name)方法。

2)Map getApplication()返回一个Map对象,该对象模拟了ServletContext实例.

3)static ActionContext getContext():获取系统的ActionContext实例。

4)Map getSession():返回一个Map对象,该对象模拟了HttpSession实例.

5)Map getParameters():获取所有的请求参数,模拟了HttpServletRequest.getParameterMap()

你也许会奇怪为什么这些方法老是返回一个Map?这主要是为了便于测试。至于它是怎么把Map对象与实际的Servlet API的实例进行转换的,这个我们根本就不要担心,因为struts2已经内置了一些拦截器来帮我们完成这一转换。

为了直接使用Servlet的API,Struts2为我们提供了以下几个接口。

1)ServletContextAware:实现该接口的Action可以直接访问ServletContext实例。

2)ServletRequestAware:实现该接口的Action可以直接访问HttpServletRequest实例。

3)ServletResponseAware:实现该接口的Action可以直接访问HttpServletResponse实例。

以上主要讲了action访问servlet,下面让我们来看一下Struts2的Action是如何实现代码复用的。就拿UserAction来说,我如果让这个action既处理用户注册(regist)又处理登录(longin)该如何改写这个action呢?改写后的UserAction如下:

?
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 example;
 
public class UserAction extends ActionSupport {
 
    private String username;
 
    private String password;
 
  public String regist() throws Exception {
 
       //…………..
 
    return SUCCESS;
 
  }
 
public String login() throws Exception {
 
       //…………..
 
    return SUCCESS;
 
  }
 
  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;
 
  }
 
}

struts.xml中的action配置
是不是这么写就ok了,当然不行我们还必须在struts.xml文件中配置一下。配置方法有两种:

1)      使用普通的方式为Action元素指定method属性.

?
1
2
3
4
5
6
7
8
9
10
11
<action name=”loginAction” class=”example.UserAction” method=”login”>
 
    <result name=”success”>/success.jsp</result>
 
</action>
 
<action name=”registAction” class=”example.UserAction” method=”regist”>
 
    <result name=”success”>/success.jsp</result>
 
</action>

2)      采用通配符的方式为Action元素指定method属性。

?
1
2
3
4
5
<action name=”*Action” class=”example.UserAction” method=”{1}”>
 
    <result name=”success”>/success.jsp</result>
 
</action>

使用通配符的方式过于灵活,下面是一个较复杂的配置情况。

?
1
2
3
4
5
<action name=”*_*” class=”example.{1}Action” method=”{2}”>
 
……….
 
</action>

其中占位符{1}与_的前一个*匹配,{2}与后一个*匹配。

基于注解方式Action配置:
下面要说的Action的配置不是在src/struts.xml中,而是用注解方式来进行配置的
前提是除了基本的那六个jar包之外,还需要一个struts-2.1.8.1\lib\struts2-convention-plugin-2.1.8.1.jar
不过struts.xml还是要有的
具体示例
Login.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
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
 <head>
  <title>Struts2登录验证</title>
  <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
  <meta http-equiv="description" content="This is my page">
  <!--
  <link rel="stylesheet" type="text/css" href="styles.css">
  -->
 </head>
  
 <body>
 <h3>Struts2登录</h3><hr/>
  <form action="${pageContext.request.contextPath}/user/login.qqi" method="post">
    <table border="1" width="500px">
      <tr>
        <td>用户名</td>
        <td><input type="text" name="loginname"/></td>
      </tr>
      <tr>
        <td>密码</td>
        <td><input type="password" name="pwd"/></td>
      </tr>
      <tr>
        <td colspan="2"><input type="submit" value="登录"/></td>
      </tr>
    </table>
  </form>
 </body>
</html>

 src/struts.xml
 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<span style="font-size: large;"><?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
  "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
  "http://struts.apache.org/dtds/struts-2.1.7.dtd">
 
<struts>
  <!-- 请求参数的编码方式 -->
  <constant name="struts.i18n.encoding" value="UTF-8"/>
  <!-- 指定被struts2处理的请求后缀类型。多个用逗号隔开 -->
  <constant name="struts.action.extension" value="action,do,go,qqi"/>
  <!-- 当struts.xml改动后,是否重新加载。默认值为false(生产环境下使用),开发阶段最好打开 -->
  <constant name="struts.configuration.xml.reload" value="true"/>
  <!-- 是否使用struts的开发模式。开发模式会有更多的调试信息。默认值为false(生产环境下使用),开发阶段最好打开 -->
  <constant name="struts.devMode" value="false"/>
  <!-- 设置浏览器是否缓存静态内容。默认值为true(生产环境下使用),开发阶段最好关闭 -->
  <constant name="struts.serve.static.browserCache" value="false" />
  <!-- 指定由spring负责action对象的创建 
  <constant name="struts.objectFactory" value="spring" />
  -->
  <!-- 是否开启动态方法调用 -->
  <constant name="struts.enable.DynamicMethodInvocation" value="false"/>
</struts></span>

 
 LoginAction.java
 

?
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
package com.javacrazyer.web.action;
 
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.ExceptionMapping;
import org.apache.struts2.convention.annotation.ExceptionMappings;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
 
import com.opensymphony.xwork2.ActionSupport;
 
/**
 * 使用注解来配置Action
 *
 */
@ParentPackage("struts-default")
// 父包
@Namespace("/user")
@Results( { @Result(name = "success", location = "/msg.jsp"),
    @Result(name = "error", location = "/error.jsp") })
@ExceptionMappings( { @ExceptionMapping(exception = "java.lange.RuntimeException", result = "error") })
public class LoginAction extends ActionSupport {
  private static final long serialVersionUID = -2554018432709689579L;
  private String loginname;
  private String pwd;
 
  @Action(value = "login")
  public String login() throws Exception {
 
    if ("qq".equals(loginname) && "123".equals(pwd)) {
      return SUCCESS;
    } else {
      return ERROR;
    }
  }
 
  @Action(value = "add", results = { @Result(name = "success", location = "/index.jsp") })
  public String add() throws Exception {
    return SUCCESS;
  }
 
  public String getLoginname() {
    return loginname;
  }
 
  public void setLoginname(String loginname) {
    this.loginname = loginname;
  }
 
  public String getPwd() {
    return pwd;
  }
 
  public void setPwd(String pwd) {
    this.pwd = pwd;
  }
 
}

 
success.jsp和error.jsp我就不贴出来了

注解配置的解释
 
  1) @ParentPackage 指定父包
  2) @Namespace 指定命名空间
  3) @Results 一组结果的数组
  4) @Result(name="success",location="/msg.jsp") 一个结果的映射
  5) @Action(value="login") 指定某个请求处理方法的请求URL。注意,它不能添加在Action类上,要添加到方法上。
  6) @ExceptionMappings 一级声明异常的数组
  7) @ExceptionMapping 映射一个声明异常

由于这种方式不是很常用,所以大家只做了解即可