Struts 2的原理深入
Struts 2 以 WebWork 优秀的设计思想为核心,吸收了Struts 1 的部分优点,建立了一个兼容 WebWork 和Struts 1 的MVC框架。
一.Struts 1的运行机制
Struts 1 框架以 ActionServlet 作为核心控制器,整个应用由客户端请求驱动。当客户端向 Web 应用发送请求时,请求将被 Struts 1 的核心控制器 ActionServlet 拦截,ActionServlet 根据请求决定是否需要调用业务逻辑控制器处理用户请求(实际上,业务逻辑控制器还是控制器,它只是负责调用模型来处理用户请求),当用户请求处理完成后,其处理结果通过 JSP 呈现给用户。
对于整个 Struts 1 框架而言,控制器就是它的核心,Struts 1 的控制器由两个部分组成:
- 核心控制器:ActionServlet,由 Struts 1 框架提供;
- 业务逻辑控制器:就是用户自定义的 Action,由应用开发者提供。
1.0 Struts 1的缺陷:
(1)支持的表现层技术单一
Struts 1只支持JSP作为表现层技术。
(2)与Servlet API严重耦合,难于测试
因为 HttpServletRequest 和 HttpServletResponse 两个参数是Servlet API,严重依赖于Web服务器。
2.0 WebWork的简介
采用了一种加松耦合的设计,让系统的Action不再与Servlet API耦合,使单元测试更加方便。并支持更多的表现层技术。
(1)核心控制器
- 核心控制器:ServletDispatcher,该控制器框架提供;
- 业务逻辑控制器:Action,该控制器由程序员提供。
(2)相对Struts 1的 Action 与 Servlet API 紧紧耦合的弱点来说,WebWork 的 Action 则完全与 Servlet API 分离,因而该 Action更容易测试。
二.Struts 2框架的核心
1、Struts 2框架的大致处理流程如下:
(1)浏览器发送请求。
(2)核心控制器 FilterDispatcher 根据请求决定调用合适的 Action。
(3)WebWork 的拦截器链自动对请求应用通用功能。
(4)回调 Action 的 execute 方法,该 execute 方法先获取用户请求参数,然后执行某种数据库操作,既可以是将数据保存到数据库,也可以从数据库中检索信息。实际上,因为Action 只是一个控制器,它会调用业务逻辑组件来处理用户的请求。
(5)Action 的 execute 方法处理结果信息将被输出到浏览器中,可以是HTML页面、图像,也可以是PDF文档或者其他文档。此时支持的视图技术非常多,既支持JSP,也支持Velocity、FreeMarker等模板技术。
Struts 2的控制器组件:
- FilterDispatcher
- 业务控制器Action
2、Struts 2的配置文件
Struts 2的配置文件有两份:
- 配置Action 的struts.xml 文件。
- 配置Struts 2 全局属性的struts.properties 文件。
(1)struts.xml文件
struts.xml 文件内定义了Struts 2 的系列Action,定义Action 时,指定该Action 的实现类,并定义该Action 处理结果与视图资源之间的映射关系。下面是struts.xml配置文件的示例:
<struts>
<!-- Struts 2 的Action 都必须配置在package 里 -->
<package name="default" extends="struts-default">
<!-- 定义一个Logon 的Action,实现类为lee.Logon -->
<action name="Logon" class="lee.Logon">
<!-- 配置Action 返回input 时转入/pages/Longon.jsp 页面 -->
<result name="input">/pages/Longon.jsp</result>
<!-- 配置Action 返回cancel 时重定向到Welcome 的Action -->
<result name="cancel" type="redirect-action">Welcom</result>
<!-- 配置Action 返回success 时重定向到MainMenu 的Action -->
<result name="cancel" type="redirect-action">MainMenu</result>
<!-- 配置Action 返回expired 时进入ChangePassword的 Action链 -->
<result name="expired" type="chain">ChangePassword</result>
</action>
<!-- 定义Logoff 的Action,实现类为lee.Logoff -->
<action name="Logoff" class="lee.Logoff">
<!-- 配置Action 返回success 时重定向到MainMenu 的Action -->
<result name="cancel" type="redirect-action">MainMenu</result>
</action>
</package>
</struts>
在上面的struts.xml文件中,定义了两个Action。定义Action 时,不仅定义了Action 的实现类,而且定义Action 的处理结果时,指定了多个result,result 元素指定execute 方法返回值和视图资源之间的映射关系。对于如下配置片段:
<result name="cancel" type="redirect-action">Welcome</result>
表示当execute 方法返回cancel 的字符串时,跳转到Welcome 的Action。
定义result 元素时,可以指定两个属性:
- type:而type 指定转向的资源类型,此处转向的资源可以是JSP,也可以是FreeMarker 等,甚至是另一个Action(这也是Struts 2可以支持多种视图技术的原因)。
- name:指定了execute 方法返回的字符串。
(2)struts.properties文件
#指定Struts 2处于开发状态
struts.devModel = false
//指定当Struts 2配置文件改变后,web框架是否重新加载Struts 2 配置文件
struts.configuration.xml.reload=true
正如上面见到的,struts.properties 文件的形式是系列的key、value对,它指定了Struts 2应用的全局属性。
3、Struts 2的标签库
Struts 2的标签库也是Struts 2 的重要组成部分。
<!-- 使用Struts 2 标签定义一个表单 -->
<s:form method="post" action="basicvalid.action">
<!-- 下面使用Struts 2标签定义三个表单域 -->
<s:textfield label="名字" name="name">
<s:textfield label="年纪" name="age">
<s:textfield label="喜欢的颜色" name="answer">
<!-- 定义一个提交按钮 -->
<s:submit/>
</s:form>
提示:Struts 2的标签库完全可以替代JSTL的标签库。而且支持表达式语言(OGNL),功能非常强大。
4、Struts 2与Spring MVC的区别
(1)拦截机制的不同
- Struts 2是类级别的拦截,每次请求就会创建一个Action,通过setter,getter把request数据注入到属性。Struts2中,一个Action对应一个request,response上下文,在接收参数时,可以通过属性接收,这说明属性参数是让多个方法共享的。
- SpringMVC是方法级别的拦截,一个方法对应一个Request上下文,所以方法直接基本上是独立的,独享request,response数据。
(2)底层框架的不同
- Struts2采用Filter(StrutsPrepareAndExecuteFilter)实现。
- SpringMVC(DispatcherServlet)则采用Servlet实现。
(3)性能方面
- Struts2是类级别的拦截,每次请求对应实例一个新的Action,需要加载所有的属性值注入。
- SpringMVC实现了零配置,由于SpringMVC基于方法的拦截,有加载一次单例模式bean注入。所以,SpringMVC开发效率和性能高于Struts2。
三.Struts 2的安装与使用
1、使用maven来创建项目
注意:Eclipse中建立Maven项目后,Java Resources资源文件下没有src/main/java文件夹
2、pom引入依赖jar包
<!-- struts2依赖包 -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.3.14</version>
</dependency>
</dependencies>
3、在src/main/java目录下新建一个UserAction.java
package strutsStady;
import java.io.UnsupportedEncodingException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
/**
*
* @ClassName: UserAction.java
* @Description:
* @author cjwang
* @version 2019年3月1日上午11:51:22
*
*/
public class UserAction extends ActionSupport {
private static final long serialVersionUID = 1L;
public String execute(){
return SUCCESS;
}
public String login() {
try {
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=utf-8");
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("name->" + username + ",password->"
+ password);
if ("admin".equals(username) && "123456".equals(password)) {
return SUCCESS;
} else {
return "login";
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return SUCCESS;
}
}
4、在src/main/resources目录新建配置struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<constant name="struts.i18n.reload" value="false" />
<constant name="struts.devMode" value="false" />
<include file="struts-default.xml" />
<package name="default" extends="struts-default" namespace="/">
<action name="login" class="strutsStady.UserAction" method="login">
<result name="success">index.jsp</result>
<result name="login">login.jsp</result>
</action>
</package>
</struts>
5、在WEB-INF/web.xml里配置
<web-app>
<display-name>Archetype Created Web Application</display-name>
<init-param>
<param-name>config</param-name>
<param-value>../../resources/struts.xml</param-value>
</init-param>
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
</web-app>
注意:解决The content of element type "web-app" must match "(icon?display
6、新建两个页面内容如下:
login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登录界面</title>
</head>
<body>
<form action="login" method="post">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username" /></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="text" name="password" /></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="登录" /> <input
type="reset" value="重置" /></td>
</tr>
</table>
</form>
</body>
</html>
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Hello Maven</title>
</head>
<body>
<p>大家好,欢迎进入Maven Struts2应用!</p>
</body>
</html>