从上一节课的第一个Struts程序的执行过程可知:Struts 的中心控制器接受所有来自客户端的请求,并根据系统的配置(struts.xml)路由HTTP请求到其它Action对象。在这些Action对象中会完成所有的业务操作,比如用户登录验证等。处理完毕后,由再由Struts的Controller Servlet根据配置转向到适当的JSP页面,将处理结果显示给用户。
因此,这过程中有两个节点是非常重要的:
一个是:struts.xml的配置文件;
另一个:Action控制类实现业务;
因此,我们本章重点来说说这两个部分。并通过例子,实现一个基于MVC模式的用户登录功能。
- 1.struts.xml
1.1.标签:定义常量;(建议:在struts.xml中定义常量)
1.2.标签:使用包配置来声明Action和拦截器;
1.3.标签:按功能拆分structs.xml配置文件; - 2.Action控制类的实现
2.1.action控制器的实现(建议:继承ActionSupport类)
2.2.action控制器在structs.xml中的声明;
2.5.action返回的结果类型(dispatcher表示转发到;redirect表示重定向到)
2.3.action访问Servlet API(即:获取上下文数据)(建议:通过ActionContext类访问Servlet)
2.4.action如何从View中获取参数- 3.案例:基于MVC模式的用户登录功能实现
1.struts.xml配置文件
struts.xml是Struts2的核心配置文件,主要用来配置Action和请求的对应关系,以及配置逻辑视图和物理视图资源的对应关系。
通常放在项目的WEB-INF/classes目录下,可以被Struts2框架自动加载。
1.1.定义常量
在写程序时,有些变量是基本不会变且需要经常使用,此时我们就把它们统一成为常量,方便项目中的各个地方使用,例如:链接字符串、PI值等。
在Struts中要定义常量有三种方法,具体如下:
- [v] 在struts.xml文件中,通过
来配置常量;
元素 - [ ] 在struts.properties文件中,通过key-value键值对配置常量;
- [ ] 在web.xml文件中,通过
元素的 子元素指定一个Struts2常量。
1.2.包配置
Struts框架的核心组件是由Action和拦截器组成,为了有效和组织管理,Struts引入了包配置
来进行声明和管理所有的Action和拦截器。
其主要的属性如下:
1.3.包含配置
由于Struts框架是通过配置文件的形式来定义路由,一旦项目规模较大,所涉及对象就会很多,为了更好的进行组织和管理这些路由,Struts允许把配置文件进行拆分,即:将一个配置文件按业务或模块可分解成多个配置文件。
但Struts2默认只加载WEB-INF/classes下的struts.xml文件,因此,我们需要在struts.xml文件中通过
引入所有子配置文件。如下图所示:
2.Action控制类
2.1.Action控制类的实现方式
Action实现对用户请求的处理,因此也被称为:业务逻辑控制器。
在Struts2中,提供了3种方式实现Action控制类:
方式一:POJO(自定义简单的Java对象)
- POJO,直接自定义一个简单的Java对象进行实现,这是最原始的方式。编写Action类需要提供getter/getter方法和公共的默认无参数构造方法以及一个execute()方法。
方式二:实现Action接口
- 实现Action接口,通过编写Action类实现Action接口,复写execute()方法。Action接口定义了5个字符串常量,它的作用是统一excecute()方法的返回值。
方式三:继承ActionSupport类 (推荐)
- 实现Action接口,通过继承ActionSupport类,重写execute()方法。ActionSupport实现Action等多个接口。所以提供了许多默认的方法,如国际化、数据校验、参数封装等。
import com.opensymphony.xwork2.ActionSupport;
public class UserLoginAction extends ActionSupport {
public String execute() throws Exception {
return super.execute();
}
}
}
2.2.action控制器在structs.xml中的声明;
实现了Action后,就要告诉Web项目,怎么样的请求该丢给哪个Action来处理,这就是需要Struts.xml中对Action进行配置声明了。
配置Action映射将一个请求的URL映射到一个Action类,即把一个URL请求对应到一个Action类,以便在Action类中实现请求的业务处理。
在struts.xml文件中,通过
struts.xml的例子::
/success.jsp
/error.jsp
/message.jsp
/message.jsp
2.5.action返回的结果类型
在struts.xml文件中,使用
/success.jsp
也可简化为:
/success.jsp
注意:在Result配置中,
使用绝对地址:使用斜杠 / 开头,例如:/success.jsp ,表示当前Web项目的上下文路径。
使用相对地址:不使用 / 开头,例如:success.jsp ,表示当前Action同级目录下的。
Struts2中预定义了一些常用的ResultType,如下图所示:
其中,
dispatcher结果类型用于表示转发,因此在用户的整个请求/响应过程中,都会保持是同一个请求对象。反映为:URL地址不跳转到JSP页面。
redirect结果类型用于重定向到指定的结果资源(可以是JSP,也可以是另一个Action)。反映为:URL地址跳转到新的URL上。
2.3.action访问Servlet API(即:获取上下文数据)
在Struts2中,Action不直接访问Servlet API,这为前后端解耦合,实现分工带来很大好处,例如:前后段独立开发,可以对单个Action进行单元测试等。
如果想获取上下文数据(例如:Session),那该如何处理呢?
方法一:通过ActionContext上下文对象(推荐)
通过实例化ActionContext对象,可以实现包括parameter、request、session、application的获取。其常用方法如下图所示:
例如,使用ActionContext对象向请求request、session、Application中都分别写入一个name参数,代码如下:
import com.opensymphony.xwork2.ActionContext;
public class UserLoginAction extends ActionSupport {
public String execute() throws Exception {
//获取Context对象
ActionContext context = ActionContext.getContext();
//将姓名name作为参数写入request请求中
context.put("name", "用户1");
//将姓名name作为参数写入session中
context.getSession().put("name", "用户1");
//将姓名name作为参数写入Application中
context.getApplication().put("name", "用户1");
return 0;
}
}
方法二:实现特定接口,从而获取Servlet API的实例
Struts2中还提供了一系列接口,通过实现这些接口从而获取Servlet API实例,如:
(1)ServletRequestAware:实现该接口的Action可以直接访问Web应用的HttpServletRequest实例。
(2)ServletResponseAware:实现该接口的Action可以直接访问Web应用的HttpServletResponse实例。
(3)SessionAware:实现该接口的Action可以直接访问Web应用的HttpSession实例。
(4)ServletContextAware:实现该接口的Action可以直接访问Web应用的ServletContext实例。
方法三:ServletActionContext类的静态方法直接获取
Struts2框架还提供了ServletActionContext类实现直接访问Servlet API的直接访问,其提供的主要几个静态方法如下:
- static HttpServletRequest getRequest():获取Web应用的HttpServletRequest对象。
- static HttpServletResponse getResponse():获取Web应用的HttpServletResponse对象。
- static ServletContext getServletContext():获取Web应用的ServletContext对象。
- static PageContext getPageContext():获取Web应用的PageContext对象。
2.4.action如何从View中获取参数
JSP页面发送请求,经过struts.xml中的配置信息,把URL映射到对应的Action对象中进行处理。
那么Action对象如何获取从JSP页面中传来的参数呢?
Struts框架为我们提供了两种方式获取JSP页面传来的参数:
方法一:字段驱动(属性驱动)的方式:
在Action类定义时,在其内部定义与JSP页面参数对应的名称的属性成员。当请求匹配到该Action时,会自动把JSP页面参数自动匹配到对应名称的属性成员中。因此,属性成员的数据类型必须与JSP页面参数的数据类型一致。例如:
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport {
//与表单中传输参数同名的username
private String username;
//与表单中传输参数同名的password
private String password;
@Override
public String execute() throws Exception {
return 0;
}
}
方法二(改良版):JavaBean驱动
既然Action中的属性成员与JSP页面中的传输参数一一对应,能否再进一步,把这些对应的数据封装称为一个实体类对象,专门用来作为传输数据的载体,这就是属性驱动的改良版——JavaBean驱动。
以JavaBean来实现,所封装的属性和表单的属性一一对应,JavaBean将成为数据传递的载体来进行数据的传递。
跟上面方法一一样,我们需要传输登录时用户所输入的用户名username和密码password两个参数,为此我们定义一个实体类对象User,专门用于存储传输的数据。具体如下:
User实体类:
package cn.demo.domain;
public class User {
private String username;
private String password;
// username的getter和setter方法
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
// password的getter和setter方法
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Action中访问实体类对象,从而获取页面传来的数据:
import cn.demo.domain.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class UserLoginAction extends ActionSupport {
private User user; //定义User实体类对象
// user属性的getter和setter方法
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String execute() throws Exception {
//获取Context对象
ActionContext context = ActionContext.getContext();
if ("itcast".equals(user.getUsername())
&& "123".equals(user.getPassword())) {
//将用户名和密码放入session中
context.getSession().put("username", user.getUsername());
context.getSession().put("password", user.getPassword());
return SUCCESS;
} else {
context.getSession().put("error", "用户登录失败!");
return ERROR;
}
}
}
方法三:模型驱动(通过接口,实现泛化的模型)
实现ModelDriven接口来接收请求参数,Action类必须实现ModelDriven接口,并且要重写getModel()方法,这个方法返回的就是Action所使用的数据模型对象。
模型驱动方式通过JavaBean模型进行数据传递。只要是普通的JavaBean,就可以充当模型部分。
采用这种方式,JavaBean所封装的属性与表单的属性一一对应,JavaBean将成为数据传递的载体。Action类通过get()的方法来获取模型,其中“”代表具体的模型对象。
作者: 肖sir@ZHBIT
2018 年 09月 27日