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,由应用开发者提供。

Struts 2的原理深入

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的安装与使用

Struts 2的原理深入

1、使用maven来创建项目

Struts 2的原理深入

注意: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>