`
liufei.fir
  • 浏览: 676051 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

struts2的基础三(action的讲解)

阅读更多
在struts2中,Action不同于struts1.x中的Action。在struts2中 Action并不需要继承任何控制器类型或实现相应接口。比如struts1.x中的Action需要继承Action或者 DispatcherAction。

同时struts2中的Action并不需要借助于像struts1.x中的ActionForm获取表单的数据。可以直接通过与表单元素相同名称的数据成员(需要存在符合命名规范的set和get方法)获取页面表单数据。

虽然struts2中的Action原则上不用继承任何类型。但是一般需要实现 com.opensymphony.xwork2.Action接口或者继承 com.opensymphony.xwork2.ActionSupport类型,然后重写execute方法。通常更愿意去继承 ActionSupport类型,这样我们可以在我们的控制器中增加更多的功能。因为ActionSupport本身不但实现了Action接口,而且实现了其他的几个接口,让我们的控制器的功能更加强大,例如:

com.opensymphony.xwork2.Validateable:提供了validate方法,可以对action中的数据进行校验

com.opensymphony.xwork2.ValidationAware:提供了 addFieldError方法,可以存取action级别或者字段级别的错误消息

com.opensymphony.xwork2.TextProvider:提供了获取本地化信息文本的方法getText

com.opensymphony.xwork2.LocaleProvider:提供了getLocale 方法,用于获取本地信息

从以上我们可以看到,继承ActionSupport,可以完成更多的工作。

例如上面的例子,例如我们需要判断输入文本框的内容,输入的内容长度必须在6-10之间。那么我们可以增加校验工作,利用validate方法。更改后的代码如下:

package com.frank.action;

import com.opensymphony.xwork2.ActionSupport;
public class HelloWorldAction extends ActionSupport {
    private String message;
    private String username;
    @Override
    public String execute() throws Exception {
        this.message="Hello World:"+this.username;
        return SUCCESS;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    @Override
    public void validate() {
        if(this.username.trim().length()<6||this.username.trim().length()>10){
            addFieldError("user.username","the length is invalid");
        }
    }
}
由于实现了validate方法,这样当请求一个控制器(在这里为helloWorld.action)的时候,首先执行validate方法,如果有错误信息增加到action中,那么就不继续执行Action,返回INPUT,否则就继续执行Action。在本例中,首先判断用户名称是否在合法的长度范围,如果不在增加错误信息,返回INPUT(默认返回)。

因为有错误返回INPUT,所以此Action在配置文件中应该定义INPUT转发路径

<action name="helloWorld" class="com.frank.action.HelloWorldAction">
                <result name="success">display.jsp</result>
                <result name="input">helloWorld.jsp</result>
            </action>

出现错误时,返回到helloWorld.jsp,并在此页面中显示控制器中所注册的错误消息,更改后的页面如下(黑色字体为增加的)

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'helloWorld.jsp' starting page</title>

<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<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>
<h1><s:fielderror/></h1><br>
<form action="helloWorld.action">
username:<s:textfield name="username"/>
<br>
<s:submit align="left"/>
</form>
</body>
</html>

当前Action执行的核心方法都是execute,有时我们可以能希望同一个控制器做不同的工作。例如在对一个表分别进行CRUD操作时,我们可能需要根据不同的情况执行4种操作,这样一个execute做起来比较麻烦,我们可以定义我们自己的方法,然后控制器有选择的执行。类似struts1.x中的DispatcherAction控制器一样。

这时我们自定义的操作方法,必须和execute具有相同的签名,只是名称不同而已。例如在前面的例子中我们增加一个超链接,让它同样请求helloWorld.action,但是在控制器端执行自己定义的方法myExecute。注意在这里先去掉验证方法 validate,取消验证。修改后代码如下:

package com.frank.action;

import com.opensymphony.xwork2.ActionSupport;
public class HelloWorldAction extends ActionSupport {
    private String message;
    private String username;
    @Override
    public String execute() throws Exception {
        this.message="Hello World:"+this.username;
        return SUCCESS;
    }
    public String myExecute() throws Exception{
        this.message="Good Morning !!!";
        return SUCCESS;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }  
}

此时我们需要增加新的Action配置,并指明调用的方法

<action name="otherHelloWorld" class="com.frank.action.HelloWorldAction" method="myExecute">
                <result name="success">display.jsp</result>
            </action>

在helloWorld.jsp中增加超链接,强求新增加的Action

<a href="otherHelloWorld.action">OtherAction</a>

新的请求页面:

点击超链接后,请求新的Action,结果如下:

由于请求的新Action在配置中指明了所调用的方法为myExecute,所以在控制器端不再执行 execute方法,转而执行myExecute,所以输出结果为
Good Morning !!!

同样存在另外一种形式请求新的Action:

即:Action名!新的方法名.action,刚才的超链接可以更改成:<a href="helloWorld!myExecute.action">OtherAction</a>

表单数据的提交

表单数据的提交在Web编程中是非常重要的。控制器需要获取用户在表示层(页面)所提交的数据,然后进行下一步操作。比如在用户登录操作中,控制器需要获取用户在页面中输入的“用户名称”和“密码”数据,来决定下一步的验证。

获取表单提交的数据有两种方式:

第一种为使用中介对象(我通常把它称为包装表示层数据的包裹)。比如在strtus1.x中使用各种 ActionForm来封装页面数据。使用中介对象可以同时在中介对象的里面增加诸如:验证、过滤、日志记录等附加工作。但是同样带来了类数量的膨胀,存在各种各样的ActionForm类(LoginForm、PersonForm等等),不利于项目的维护管理。

第二种是直接使用控制器中的域对象,即直接使用控制器中的数据成员获取表示层的数据,在struts2种支持此种方式。但必须保证相应的数据成员和表示层提交的数据名称一致,并且具有符合命名规范的setter和getter方法。这样可以达到“内省”。直接使用域对象可以减少类的膨胀,如果只是简单的获取提交的数据则建议直接使用域对象。

在此我们还是同一个非常简单的(我经常采用的)登录操作来体会开发过程。可以通过此操作扩展到复杂的操作,其实原理一样。还是从头开始(省去准备工作的步骤,直接进入开发过程):

首先创建一个登录页面login.jsp,代码内容如下:

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'login.jsp' starting page</title>

<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<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>

<h1><s:fielderror/></h1>
<h1>User Login</h1><br>
<form action="login.action">
    Username:<s:textfield name="username"/><br>
    Password:<s:textfield name="password"/><br>
    <input type="submit" value="Login"/>
</form>
</body>
</html>
 

<s:fielderror/>用于显示所注册的错误信息

接着建立Actiion类:LoginAction,在此我们通过域对象获取表示层提交的数据,也就是在控制器中声明两个数据成员username和password来得到表示层的数据,然后在execute方法中进行验证工作(当然,一般规范开发需要在业务类中进行实际验证),在此简便此操作。同时重写validate方法在Action实际执行前进行验证工作,具体的验证方式由自己的业务决定。代码如下:

package com.frank.action;

import com.opensymphony.xwork2.ActionSupport;

public class LoginAction extends ActionSupport {

    private String username;
    private String password;
    @Override
    public String execute() throws Exception {
        if(this.username.equals("admin")&&this.password.equals("admin")){
            return SUCCESS;
        }
        return INPUT;
    }

    @Override
    public void validate() {
        if(this.username==null||this.username.trim().length()<1){
            addFieldError("no.username","must input username");
        }
        if(this.password==null||this.password.trim().length()<1){
            addFieldError("no.password","must intpu 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;
    }

}

新建一个index.jsp页面,用户在用户登录成功后显示,内容非常的简单,就是一条“Login Success!!!”

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<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>
<h1>Login Success!!!</h1>
</body>
</html>
 

现在可以开始配置Action了,在struts.xml中增加新的Action,配置如下:

<action name="login" class="com.frank.action.LoginAction">
                    <result>index.jsp</result>
                    <result name="input">login.jsp</result>
            </action>

可以部署和运行了,启动login.jsp

如果在任意一个文本框中不输入内容或者输入空白内容,则返回当前页面,并错误提示

如果输入的用户名和密码错误,同样返回到当前页面

如果输入的用户和密码正确,则到达index.jsp

注意以上所有的提交方式都为默认GET提交方式

封装表示层数据到业务类中

在实际的开发过程中,我们可以用业务类对象获取表示层数据,这样我们可以在业务类中实现更多的业务方法(比如登录、注册)。而由控制器去调用业务类。因为struts2同样遵循的是MVC模式,所以我们不应该让控制器去做实际的业务工作,而是调用Model,而这里同样用Model获取表示层的数据。

这样我们可以新建一个类User,在这里声明数据成员username和password,并增加一个验证方法login,用于验证登录。User类的代码如下:

package com.frank.rule;

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;
}
public boolean login(){
if(this.username.equals("admin")&&password.equals("admin")){
return true;
}
return false;
}
}

既然我们用User获取用户的数据,这样我们需要在Action增加User类型的数据成员,而去掉原来的 username和password,重构的代码如下:

package com.frank.action;

import com.opensymphony.xwork2.ActionSupport;
import com.frank.rule.User;
public class LoginAction extends ActionSupport {

private User user;
@Override
public String execute() throws Exception {
if(user.login()){
return SUCCESS;
}
return INPUT;
}

@Override
public void validate() {
if(user.getUsername()==null||user.getUsername().trim().length()<1){
addFieldError("no.username","must input username");
}
if(user.getPassword()==null||user.getPassword().trim().length()<1){
addFieldError("no.password","must intpu password");
}

}
public User getUser() {
return user;
}

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

}
 

在这里页面的数据不再由控制器的username和password数据成员获取,转而有其中的user对象进行封装。所以应该修改login.jsp的提交表单,修改后的结果如下:

<form action="login.action">
        Username:<s:textfield name="user.username"/><br>
        Password:<s:textfield name="user.password"/><br>
        <input type="submit" value="Login"/>
</form>

注意此时提交表单中的文本框名称必须为user.username和user.password,这样所输入的内容会由控制器的user对象获取。

这里可能有一个疑问,就是在控制器中并没有在任何时候创建User对象,那么内容是如何获得的呢?难道不会抛出NullPointerException?此时当请求控制器,并设置user对象的username数据成员时,action依次调用下方法:

user.getUser();

user.setUser(new User());

user.getUser().setUsername("admin");

从上可以看出,首先struts2会尝试获取user对象,如果不存在则执行第二步创建一个新对象,然后将内赋给数据成员

重新部署,运行,所得到的效果和前一个相同。

使用Servlet相关对象

在进行Web编程时,很多时候需要使用Servlet相关对象,例如:HttpServletRequest、HttpServletResponse、HttpSession、ServletContext。我们可以将一些信息存放到session中,然后在需要的时候取出。

在struts1.x的Action中,可以通过HttpServletRequest和 HttpServletResponse参数来得到Servlet相关对象,进而使用。那么在struts2种如何获取?

我们可以使用com.opensymphony.xwork2.ActionContext类来完成上述操作。此类的getContext方法可以得到当前Action的上下文,也就是当前Action所处的容器环境,进而得到相关对象。

同时也可以使用org.apache.struts2.ServletActionContext类获得,例如:

HttpServletRequest req=ServletActionContext.getRequest();

下面更改前面的登录操作,在登录成功后,将登录人员的信息保存到session范围中,在index.jsp 页面中取出,我们可以重构我们的Action,在重构前,首先新增一个BaseAction,作为所有Action的父类,在其中增加方法获取 Servlet相关对象,这样所有的子类都拥有了相应的方法。代码如下:

package com.frank.action;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.ServletActionContext;
public class BaseAction extends ActionSupport {
    public HttpServletRequest request(){
        return ServletActionContext.getRequest();
    }
    public HttpServletResponse response(){
        return ServletActionContext.getResponse();
    }
    public ServletContext application(){
        return ServletActionContext.getServletContext();
    }
    public HttpSession session(){
        return ServletActionContext.getRequest().getSession();
    }
}

然后重构LoginAction,继承自BaseAction,并在登录成功后再session范围保存用户名信息,以便index.jsp读取,重构后代码如下:

package com.frank.action;

import com.opensymphony.xwork2.ActionSupport;
import com.frank.rule.User;
public class LoginAction extends BaseAction {

private User user;
@Override
public String execute() throws Exception {
if(user.login()){
this.session().setAttribute("username", user.getUsername());
return SUCCESS;
}
return INPUT;
}

@Override
public void validate() {
if(user.getUsername()==null||user.getUsername().trim().length()<1){
addFieldError("no.username","must input username");
}
if(user.getPassword()==null||user.getPassword().trim().length()<1){
addFieldError("no.password","must intpu password");
}

}
public User getUser() {
return user;
}

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

}
在index.jsp中增加读取session中保存内容的代码,如下:

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<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>
<h1>Login Success!!!</h1>
<br>
<h1>Username:<%=session.getAttribute("username") %></h1>
</body>
</html>

重新部署,运行,结果如下:
分享到:
评论

相关推荐

    Struts 2.0的Action讲解

    Struts 2.0的Action讲解 com.opensymphony.xwork2.ActionSupport类

    struts2帮助文档

    struts2的Action讲解 struts2中的国际化 struts2转化器 struts2实现表单数据校验 struts2的基石-拦截器 struts2中实现IOC struts2中实现文件上传 struts2中实现CRUD struts2中的OGNL struts2的新表单标志的使用 ...

    Struts 2_0的Action讲解

    Struts 2_0的Action讲解

    Struts2的Action讲解

    Struts 1.x与Struts 2.0的Action模型很大的区别以及举了几个经典的小例子帮助理解Struts2 Action。

    Struts2_Action

    Struts2基础知识---基于Struts2的action讲解课件,详细讲解了action---xml的配置

    Struts2_Action-1

    Struts2基础知识讲解PPT课件---Struts2_Action,详细介绍action的工作原理

    Struts2基础教程

    讲解Struts2入门基础,包括Action、Result、ValueStack等,Struts2初学者会有帮助

    struts2技术内幕+struts2权威指南

    然后厘清了Web开发中极易混淆的一些重要概念,以及Struts2的核心技术、宏观视图、微观元素、配置元素等,提纲挈领地对Struts2进行了多角度的讲解。核心技术篇首先分析了Struts2中多种具有代表性的设计模式,然后对...

    个人认为目前最完备的Struts2教程

    03 Struts 2.0的Action讲解 04 在Struts 2.0中国际化(i18n)您的应用程序 05 转换器(Converter)——Struts 2.0中的魔术师 06 在Struts 2.0中实现表单数据校验(Validation) 07 Struts 2的基石——拦截器...

    struts2入门教程

    ·Struts 2.0的Action讲解 ·Struts2国际化(i18n)您的应用程序 ·Struts2.0转换器(Converter) ·Struts 2.0中实现表单数据校验(Validation) ·拦截器(Interceptor) ·Struts 2中实现IoC ·Struts 2中...

    struts2 in action 中文版

    之后,深入讲解了Struts 2的核心概念和组件,剖析了每个组件的功能,并提供了一个全功能的Struts 2 Web示例应用程序——Struts 2公文包示例应用程序。最后讲解了从核心组件中挑选出来的高级技术以及一些框架的高级...

    struts2.0中文教程

    03 Struts 2.0的Action讲解 04 在Struts 2.0中国际化(i18n)您的应用程序 05 转换器(Converter)——Struts 2.0中的魔术师 06 在Struts 2.0中实现表单数据校验(Validation) 07 Struts 2的基石——拦截器...

    struts2笔记之动态调用Action指定方法及默认Action

    详细讲解struts2中单个action中多个处理逻辑的配置方法, 以及默认Action的配置.

    Struts 2.0系列(MAX)

    Struts 2.0的Action讲解 在Struts 2.0中国际化(i18n)您的应用程序 转换器(Converter)——Struts 2.0中的魔术师 在Struts 2.0中实现表单数据校验(Validation) Struts 2的基石——拦截器(Interceptor) 在...

    Struts2.0中文教程权威版

    03 Struts 2.0的Action讲解 04 在Struts 2.0中国际化(i18n)您的应用程序 05 转换器(Converter)——Struts 2.0中的魔术师 06 在Struts 2.0中实现表单数据校验(Validation) 07 Struts 2的基石——拦截器...

    struts2之Action访问Servlet API

    详细讲解了struts2的Action访问ServletAPI的几种方法.

    Struts2例子3

    Struts 2.0的Action讲解

    struts2详解

    对struts2的内部详细讲解 例如:struts xml struts2的action使用 以及国际化 验证等技术

    struts的课件,及详细讲解

    struts-config.xml是Struts的主要配置文件,在该文件中,可以配置数据源、form-bean、action和plug-in(插件)和资源文件的信息。其文件主要结构如下所示: ...

    Struts In Action中文版

    Struts In Action中文版,通过实例详细描述了struts的使用和框架讲解,并对文件配置常用对象做了详细的描述。

Global site tag (gtag.js) - Google Analytics