【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
Action
一、Action基础
Action是什么
- 在Struts2中一个Action类就代表一次请求或者调用,每个请求的动作都对应于一个相应的Action,一个Action类是一个独立的工作单元。
- Action就是用来处理请求对象的,相当于Servlet中的Servlet类。
- 在MVC模式中充当着模型的角色。
Action的基本配置
- 不管Action采用何种实现方式,要正确运行,都需要在Struts.xml中进行配置,这是使用Action的基础。
struts.xml的配置package struts2.com.test; import com.opensymphony.xwork2.Action; public class HellowordAction implements { public String execute() { return "success"; } }
class的值为Action类的全路径名:一个类的全路径名=这个类的包名+“.”+类名。<struts> <package name="verify" extends="struts-default"> <action name="registerAction" class="struts2.com.test.HellowordAction"> .... </action> </package> </struts>
二、Action的实现
POJO的实现
- 在Struts2中,Action可以不实现任何特殊的接口或者继承特殊的类,仅仅是一个POJO就可以,但是要有一个公共的为空的构造方法,其实缺省的构造方法就可以了,还要有一个execute方法,格式如下:
public String execute() throws Exception{ .... }- 可见的修饰符public
- 不需要传入参数
- 返回一个字符串
- 可以抛出异常,也可以不抛出。
- 实际上我们在书写Action类的时候一般会继承ActionSupport类或者实现Action接口,因为他们为我们封装了一些方法和常量,可以让我们更简单更好的开发功能。具体的方法和字段可以查看struts2的官方帮助文档。
- POJO实质上可以理解为简单的实体类,顾名思义POJO类的作用是方便程序员使用数据库中的数据表,对于广大的程序员,可以很方便的将POJO类当做对象来进行使用,当然也是可以方便的调用其get,set方法。POJO类也给我们在struts框架中的配置带来了很大的方便。
继承ActionSupport类
- 由于Xwork的Action接口非常简单,为程序员提供的帮助有限,因此,在实际开发中我们都会使用继承ActionSupport类来实现Action的方式。
import com.opensymphony.xwork2.ActionSupport; public class ActionSupport_1 extends ActionSupport { ..... }- 本身实现了Action接口,所以继承ActionSupport相当于实现Action接口
- ActionSupport也提供了很多新的方法。
- 示例基本的数据验证
- 要实现数据验证的功能,只需要在Action类中重写vaildate方法即可:在该方法内部进行校验,如果不满足要求,则提示。我这里只用到校验密码,所以只写了密码
- 提交页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>登陆验证</title> </head> <body> <form action="action_1" > <input type="password" name="password" placeholder="请输入密码"> <input type="submit" value="提交"> </form> </body> </html>- Action类
package struts2.com.test; import com.opensymphony.xwork2.ActionSupport; public class ActionSupport_1 extends ActionSupport { private String password; public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String execute() throws Exception { System.out.println("登陆成功"); return SUCCESS; } @Override public void validate() { if (password == null || password.trim().length() == 0) this.addFieldError("passwordmsg", "密码为空"); if (password.trim().length() < 6) this.addFieldError("passwordmsg", "密码长度必须大于6位"); } }
如果验证结果不符合要求,那么使用父类提供的addFieldError方法来添加验证的错误信息。addFieldError方法有两个参数,前面是key,后边是具体消息。- validate方法没有返回值,那么当验证后,如果有数据没有通过验证,该返回到什么页面呢?
- 这就需要在struts.xml中的Action配置中,添加一个input的result配置,也就是说当有数据没有通过验证即调用了addFieldError方法,这时就会自动陶砖回到该action标签中名称为input的result所配置的页面。
<action name="action_1" class="struts2.com.test.ActionSupport_1"> <!--suppress Struts2ModelInspection --> <result name="success">hellowordactionsuccess.jsp</result> <!--suppress Struts2ModelInspection --> <result name="input">hellowordactionerror_1.jsp</result> </action>- 从以上程序的运行后发现,当我们的请求调用该Action时会先调用validate方法,当该方法运行结束后,调用Action的运行环境就会去判断存放消息验证的集合,如果其中有值,那么就会直接跳转到名称为input所配置的页面上。所以我们在验证不通过的时候,execute方法中的语句时没有在控制台中输出的。
- 要实现数据验证的功能,只需要在Action类中重写vaildate方法即可:在该方法内部进行校验,如果不满足要求,则提示。我这里只用到校验密码,所以只写了密码
execute方法内部实现方式
- 书写一个Action类,我们可以使用书写一个简单的POJO,也可以是实现接口Action或者继承ActionSupport类,
- 这三种方式都会默认调用Action类里的execute方法(如果action标签中没有指定method属性),所以我们看看完整的Action是怎样工作的
- 收集用户传递过来的数据,在执行execute方法之前,页面提交的值就会自动的对应到这些属性上,这个功能是拦截器做的,后面会讲到拦截器。
- 把收集到的数据组织成为逻辑层需要的类型和格式。
- 调用逻辑层接口,来执行业务逻辑处理。
- 准备下一个页面所需要展示的数据,存放在相应的地方。
- 转向下一个页面。
三、Action的数据
基本的数据对应方式
- 在struts2中,页面数据和Action有两种基本对应方式,分别是:属性驱动和模型驱动。
- 属性驱动又有两种情况:一种基本数据类型的属性对应;另一种是JavaBean风格的属性对应。为了区分它们,我们称其为:"基本数据类型的属性对应",为属性驱动,而"JavaBean风格的属性对应"为直接使用域对象。
-
属性驱动FieldDriven(基本数据类型的数据对应)
- 基本数据类型的属性对应,就是web页面上提交的属性name的值和Action的属性或者与属性相应的getter、setter相对应,这种做法就是基本数据类型的属性对应的属性驱动。比如:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>登陆验证</title> </head> <body> <form action="action_1" > <input type="password" name="password" placeholder="请输入密码"> <input type="submit" value="提交"> </form> </body> </html>package struts2.com.test; import com.opensymphony.xwork2.ActionSupport; public class ActionSupport_1 extends ActionSupport { private String password; public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String execute() throws Exception { System.out.println("登陆成功"); return SUCCESS; } }如果用户不想为每个属性都提供setter/getter方法,那么可以将字段设置为public修饰的,这样我们就能不使用set/get方法赖访问这些数据了,但是在实际的开发中我们要更好的实现封装,隐藏类的实现细节,我们最好使用private来修饰自己的属性。(我在后面的开发时发现idea中如果类的属性设置为public修饰,在jsp页面中的name属性会显示为红色,但是测试是可以使用的,没有错误)
-
属性驱动(域对象属性的对应)
- 当我们要传入多个数据的话,那么Action里面的属性也就要变多,而且每次处理一次不同的请求即不同的Action都要创建这些属性字段,所以我们可以将这些属性字段封装在一个用户类中,这个类就是用来封装这些数据的,然后再Action类中使用这个对象就好,比如:
- 用户对象
package struts2.com.verify; /** * 用户注册 */ public class UserMode { private String name;//姓名 private int age;//年龄 private String account;//账号 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } @Override public String toString() { return "name=" + name + ",age=" + age + ",account=" + account; } } - Action对象
package struts2.com.verify; import com.opensymphony.xwork2.ActionSupport; public class RegisterAction extends ActionSupport { private UserMode userMode = new UserMode(); @Override public String execute() throws Exception { System.out.println("传入的数据为=" + userMode); return SUCCESS; } public UserMode getUserMode() { return userMode; } }- 登陆界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>注册</title> </head> <body> 用户注册 <hr> <s:form action="registerAction" method="POST"> <s:textfield name="userMode.account" label="账号"/> <s:textfield name="userMode.name" label="姓名"/> <s:textfield name="userMode.age" label="年龄"/> <s:submit value="提交"/> <s:reset value="重置"/> </s:form> </body> </html>- struts.xml
<action name="registerAction" class="struts2.com.verify.RegisterAction"> <result name="success">registerSuccess.jsp</result> </action>- 成功页面,获取信息
<s:property value="userMode.name" />- 控制台输出
传入的数据为=name=李宁,age=18,account=123456
- 用户对象
- 当我们要传入多个数据的话,那么Action里面的属性也就要变多,而且每次处理一次不同的请求即不同的Action都要创建这些属性字段,所以我们可以将这些属性字段封装在一个用户类中,这个类就是用来封装这些数据的,然后再Action类中使用这个对象就好,比如:
-
模型驱动
- 这种数据对应的方式是让Action实现一个ModelDriven的接口,这个接口需要我们实现一个geyModel的方法,这个方法的返回就是Action所使用的数据模型对象。
- Action类:
package struts2.com.test; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; import struts2.com.verify.UserMode; public class ModelDriven_1 extends ActionSupport implements ModelDriven { private UserMode userMode = new UserMode(); @Override public String execute() throws Exception { System.out.println("传入的数据为=" + userMode); return SUCCESS; } @Override public Object getModel() { return userMode; } }- 注册页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>注册</title> </head> <body> 用户注册 <hr> <s:form action="modeldriven" method="POST"> <s:textfield name="account" label="账号"/> <s:textfield name="name" label="姓名"/> <s:textfield name="age" label="年龄"/> <s:submit value="提交"/> <s:reset value="重置"/> </s:form> </body> </html>- struts.xml
<action name="modeldriven" class="struts2.com.test.ModelDriven_1"> <result name="success">../verify/registerSuccess.jsp</result> </action>- 注册成功页面,获取属性信息
<s:property value="name" />- 控制台输出
传入的数据为=name=李宁,age=14,account=123
这里获取属性值和注册页面都不需要使用对象.属性名来获取值和传值,前面的两种方式都需要这样,因为使用ModelDriven的方式,一个Action只能对应一个Model,因此不需要添加前缀。
- 两种驱动方式的优点和缺点:
- 优点:基本数据类型的属性对应:简单,页面name和属性直接对应。缺点:导致Action零乱,功能不单一。
- 直接使用域对象:
- 优点:把模型数据从Action中分离出来,让Action专注于处理请求,使结构更加清晰;缺点:页面对应时必须要加Action中类对象这样的前缀,优点麻烦。
- 模型驱动
- 优点:把模型数据从Action中分离出来了,使程序结构清晰。而且页面不需要书写前缀;缺点:需要实现接口,而且把模型数据和Action做了绑定,这极大的限制了一个Action对应多数据模型的能力,当然也可以做到,就是在这个模型里面包含其他的数据模型。
- 这种数据对应的方式是让Action实现一个ModelDriven的接口,这个接口需要我们实现一个geyModel的方法,这个方法的返回就是Action所使用的数据模型对象。
-
处理多个传入值
传入一组数目不确定的字符串
我们将一组数据传入到同一个属性里去即用相同的name,然后再Action类里就会存在相同name值的字段名里存着这些提交的一组数据,那么我们获取的时候,只需要写上该字段的名称和遍历的下标就可以了。
- 比如我们使用checkbox类型的输入框
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>传入多个值</title>
</head>
<body>
<form action="collection.action">
<input type="checkbox" name="habits" value="运动" >运动
<input type="checkbox" name="habits" value="唱歌">唱歌
<input type="checkbox" name="habits" value="跳舞">跳舞
<input type="submit" value="提交">
</form>
<form action="collection.action">
<input type="checkbox" name="list" value="唱" >唱
<input type="checkbox" name="list" value="跳">跳
<input type="checkbox" name="list" value="rap">rap
<input type="submit" value="提交">
</form>
</body>
</html>
- Action类定义一个私有的数组或者集合。
package struts2.com.test;
import com.opensymphony.xwork2.ActionSupport;
import struts2.com.verify.UserMode;
import java.util.List;
public class CollectionAction extends ActionSupport {
private String[] habits;
private List<String> list;
private List<UserMode> users;
public List<UserMode> getUsers() {
return users;
}
public void setUsers(List<UserMode> users) {
this.users = users;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public String[] getHabits() {
return habits;
}
public void setHabits(String[] habits) {
this.habits = habits;
}
@Override
public String execute() throws Exception {
System.out.println(users);
return SUCCESS;
}
}
- 页面获取这些集合或者数组里的值
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>获取集合的值</title>
</head>
<body>
1:<s:property value="habits[0]" /><br>
2:<s:property value="habits[1]" /><br>
3:<s:property value="habits[2]" /><br>
4:<s:property value="list[0]" /><br>
5:<s:property value="list[1]" /><br>
6:<s:property value="list[2]" /><br>
</body>
</html>
传入一组数目不确定的域对象
域对象也就是说这些数据对应的是一个类里面的字段,当我们传入的值是对应的一个类里面的字段时我们可以这样
- 页面数据提交
<form action="collection.action">
<input type="checkbox" name="users.name" value="唱1" >唱1
<input type="checkbox" name="users.age" value="11">11
<input type="checkbox" name="users.account" value="rap1">rap1
<input type="submit" value="提交">
</form>
- Action类就和上面的类一样。
- 获取数据
7:<s:property value="users" /><br>
8:<s:property value="users" /><br>
9:<s:property value="users" /><br>
10:<s:property value="users.account" /><br>
11:<s:property value="users.name" /><br>
12:<s:property value="users[0].age" />
这里的三个users是三个同一类的对象,这三个对象是不同的,我们要获取同一对象的值就要带上对象的下标,比如:users[0].age。这样我们才能取到同一对象里的数据。
Struts拥有自动类型转换的功能,比如前端页面请求的数据为String类型,但是Action类中对应的属性字段为int类型,那么struts的拦截器会自动转换成int类型的,保存在字段里。
三、Action的配置
<package>的配置
- <package>元素可以把逻辑上相关的一组Action、Result、Intercepter等元素封装起来,形成一个独立的模块,package可以继承其他的package,也可以作为父包被引用,<package name="verify" extends="struts-default">verify这个包就继承了struts-default这个包。
- package元素有如下属性:
- name:包的名称。必须配置
- extends:要继承的包,后面配置的是被继承的包的名称。可选,默认继承struts-default包
- namespace:包的命名空间。可选
- abstract:定义包为抽象的,也就是不能包含Action的定义。可选
- namespace
- namespace配置的是包的命名空间,同一个命名空间里面不能有同名的Action。可以有效的防止action重名的冲突,因为配置了namespace后,在访问action的时候就需要添加namespace来作为action的前缀。
- 如果不配置namespace,表示是默认的namespace,那么访问的时候就不需要添加了。
- abstract
- abstract用来定义包为抽象的,也就是不能包含Action的定义,但是抽象包可以被其他包继承,因此这里面可以定义其他包需要的元素,比如:ResultType、Interceptor等。
<action>的配置
- <action>元素是<package>元素的子元素,应该配置在<package>元素里面。
- <action>元素通常需要配置name和class属性,其中name是必须的。
- <action>元素可以包含其他的子元素:比如<param>、<result>、<interceptor>、<exception-mapping>。
分模块配置
- 一个<struts>元素可以有多个<package>子元素
- 一个<package>元素可以有多个<action>子元素
- 我们常常会使用在struts.xml中引用其他的struts配置文件,这样的单独模块可以让文件更好维护比如:
<include file="verify/sturts_verify.xml"/>
默认类配置方式
- 在配置struts.xml的时候,对于<action>元素来说,name是必须的,class属性可以省略,class属性的默认值是:
com.opensymphony.xwork2.ActionSupport
四、Action的其他重要知识
Action的生命周期
Action不是在启动服务器的时候就初始化了,而是在当用户访问请求该Action的时候,才会被初始化,当请求结束后就会消失。而且每次请求都会创建一个对象,前面说过每个请求都会对应一个实例。
调用非execute方法
- 在前面说过当我们不想使用execute方法作为业务处理模型时,我们可以在action标签中配置method属性,来告诉struts找谁来处理业务。
- 在请求的url中直接指定的方式,也可以让我们不用配置method就能不使用execute方法赖处理业务。
<form action="helloword!create.action">
这里的.action可以不写,struts会自动添加的。
来源:oschina
链接:https://my.oschina.net/u/4045839/blog/3143913