Struts2_使用 Filter 作爲控制器的 MVC 應用
實現 MVC(Model、View、Controller) 模式的應用程序由 3 大部分構成:
模型:封裝應用程序的數據和業務邏輯POJO(Plain Old Java Object)// 普通的java類
**視圖:**實現應用程序的信息顯示功能JSP
**控制器:**接收來自用戶的輸入,調用模型層,響應對應的視圖組件Servlet Filter
背景
代碼
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>Insert title here</title>
</head>
<body>
<a href="product-input.action">Product Input</a>
</body>
</html>
input.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>Insert title here</title>
</head>
<body>
<form action="product-save.action" method="post">
ProductName: <input type="text" name="productName"/>
<br><br>
ProductDesc: <input type="text" name="productDesc"/>
<br><br>
ProductPrice: <input type="text" name="productPrice" />
<br><br>
<input type="submit" value="Submit"/>
<br><br>
</form>
</body>
</html>
product
public class Product {
private Integer productId;
private String productName;
private String productDesc;
private double productPrice;
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getProductDesc() {
return productDesc;
}
public void setProductDesc(String productDesc) {
this.productDesc = productDesc;
}
public double getProductPrice() {
return productPrice;
}
public void setProductPrice(double productPrice) {
this.productPrice = productPrice;
}
public Product(Integer productId, String productName, String productDesc,
double productPrice) {
super();
this.productId = productId;
this.productName = productName;
this.productDesc = productDesc;
this.productPrice = productPrice;
}
public Product() {
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Product [productId=" + productId + ", productName="
+ productName + ", productDesc=" + productDesc
+ ", productPrice=" + productPrice + "]";
}
}
detail.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>Insert title here</title>
</head>
<body>
ProductId: ${requestScope.product.productId }
<br><br>
ProductName: ${requestScope.product.productName }
<br><br>
ProductDesc: ${requestScope.product.productDesc }
<br><br>
ProductPrice: ${requestScope.product.productPrice }
<br><br>
</body>
</html>
FilterDispatcher
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
/**
* Servlet Filter implementation class FilterDispatcher
*/
public class FilterDispatcher implements Filter {
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
//1. 獲取 servletPath
String servletPath = req.getServletPath();
System.out.println(servletPath);
String path = null;
//2. 判斷 servletPath, 若其等於 "/product-input.action", 則轉發到
///WEB-INF/pages/input.jsp
if("/product-input.action".equals(servletPath)){
path = "/WEB-INF/pages/input.jsp";
}
//3. 若其等於 "/product-save.action", 則
if("/product-save.action".equals(servletPath)){
//1). 獲取請求參數
String productName = request.getParameter("productName");
String productDesc = request.getParameter("productDesc");
String productPrice = request.getParameter("productPrice");
//2). 把請求信息封裝爲一個 Product 對象
Product product = new Product(null, productName, productDesc, Double.parseDouble(productPrice));
//3). 執行保存操作
System.out.println("Save Product: " + product);
product.setProductId(1001);
//4). 把 Product 對象保存到 request 中. ${param.productName} -> ${requestScope.product.productName}
request.setAttribute("product", product);
path = "/WEB-INF/pages/details.jsp";
}
if(path != null){
request.getRequestDispatcher(path).forward(request, response);
return;
}
chain.doFilter(request, response);
}
public void init(FilterConfig fConfig) throws ServletException {}
}
使用 Filter 作爲控制器的好處
使用一個過濾器來作爲控制器, 可以方便地在應用程序裏對所有資源(包括靜態資源)進行控制訪問.
Servlet Filter比較
Servlet 能做的 Filter 都可以完成
Filter 能做的 Servlet 不可以完成, 攔截資源卻不是 Servlet 所擅長的! Filter 中有一個 FilterChain,這個 API 在 Servlet 中沒有!
Struts2概述
Struts2 是一個用來開發 MVC 應用程序的框架. 它提供了 Web 應用程序開發過程中的一些常見問題的解決方案:
對來自用戶的輸入數據進行合法性驗證
統一的佈局
可擴展性
國際化和本地化
支持 Ajax
表單的重複提交
文件的上傳下載
……
Struts2和Struts1相比
在體系結構方面更優秀:
類更少, 更高效: 在 Struts2 中無需使用 “ActionForm” 來封裝請求參數.
擴展更容易: Struts2 通過攔截器完成了框架的大部分工作. 在 Struts2 中插入一個攔截器對象相當簡便易行.
更容易測試:
即使不使用瀏覽器也可以對基於 Struts2 的應用進行測試
Struts1 升級到 Struts2
Struts2 從本質上講已不是從 Struts1 擴展而來的, 說它是一個換了品牌標籤的 WebWork 更合適
從 Struts1 升級到 Struts2:
Struts1 裏使用 ActionServlet 作爲控制器; Struts2 使用了一個過濾器作爲控制器
Struts1 中每個 HTML 表單都對應一個 ActionForm 實例. Struts2 中, HTML 表單將被直接映射到一個 POJO.
Struts1 的驗證邏輯編寫在 ActionForm 中; Struts2 中的驗證邏輯編寫在 Action 中.
Struts1 中, Action 類必須繼承 org.apache.struts.action.Action 類; Struts2 中任何一個 POJO 都可以是一個 Action 類.
Struts2 在頁面裏使用 OGNL 來顯示各種對象模型, 可以不再使用 EL 和 JSTL
下載 Struts2
Hello World
搭建 Struts2 的環境:
具體教程
加入 jar 包: 複製 struts\apps\struts2-blank\WEB-INF\lib 下的所有 jar 包到當前 web 應用的 lib 目錄下.
在 web.xml 文件中配置 struts2: 複製 struts\apps\struts2-blank1\WEB-INF\web.xml 文件中的過濾器的配置到當前 web 應用的 web.xml 文件中
在當前 web 應用的 classpath 下添加 struts2 的配置文件 struts.xml: 複製 struts1\apps\struts2-blank\WEB-INF\classes 下的 struts.xml 文件到當前 web 應用的 src 目錄下.
添加 DTD 約束
struts-2.3.4-all\struts-2.3.4\src\core\src\main\resources
代碼詳情
src下的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>
<!--
package: 包. struts2 使用 package 來組織模塊.
name 屬性: 必須. 用於其它的包應用當前包.
extends: 當前包繼承哪個包, 繼承的, 即可以繼承其中的所有的配置. 通常情況下繼承 struts-default
struts-default 這個包在 struts-default.xml 文件中定義.
namespace 可選, 如果它沒有給出, 則以 / 爲默認值.
若 namespace 有一個非默認值, 則要想調用這個包裏的Action,
就必須把這個屬性所定義的命名空間添加到有關的 URI 字符串裏
http://localhost:8080/contextPath/namespace/actionName.action
-->
<package name="helloWorld" extends="struts-default">
<!--
配置一個 action: 一個 struts2 的請求就是一個 action
name: 對應一個 struts2 的請求的名字(或對一個 servletPath, 但去除 / 和擴展名), 不包含擴展名
class 的默認值爲: com.opensymphony.xwork2.ActionSupport
method 的默認值爲: execute
result: 結果.
-->
<action name="product-input"
class="com.opensymphony.xwork2.ActionSupport"
method="execute">
<!--
result: 結果. 表示 action 方法執行後可能返回的一個結果. 所以一個 action 節點可能會有多個 result 子節點.
多個 result 子節點使用 name 來區分
name: 標識一個 result. 和 action 方法的返回值對應. 默認值爲 success
type: 表示結果的類型. 默認值爲 dispatcher(轉發到結果.)
-->
<result name="success" type="dispatcher">/WEB-INF/pages/input.jsp</result>
</action>
<action name="product-save" class="com.atguigu.struts2.helloworld.Product"
method="save">
<result name="details">/WEB-INF/pages/details.jsp</result>
</action>
<action name="test" class="com.atguigu.struts2.helloworld.Product" method="test">
<result>/index.jsp</result>
</action>
</package>
</struts>
src/product Product
package product;
public class Product {
private Integer productId;
private String productName;
private String productDesc;
private double productPrice;
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getProductDesc() {
return productDesc;
}
public void setProductDesc(String productDesc) {
this.productDesc = productDesc;
}
public double getProductPrice() {
return productPrice;
}
public void setProductPrice(double productPrice) {
this.productPrice = productPrice;
}
@Override
public String toString() {
return "Product [productId=" + productId + ", productName="
+ productName + ", productDesc=" + productDesc
+ ", productPrice=" + productPrice + "]";
}
public String save(){
System.out.println("save:"+this);
return "details";
}
}
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>Insert title here</title>
</head>
<body>
<a href="product-input.action">Product Input</a>
</body>
</html>
WEB.INF/pages
details.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>Insert title here</title>
</head>
<body>
ProductId: ${productId }
<br><br>
ProductName: ${productName }
<br><br>
ProductDesc: ${productDesc }
<br><br>
ProductPrice: ${productPrice }
<br><br>
</body>
</html>
//input.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>Insert title here</title>
</head>
<body>
<form action="product-save.action" method="post">
ProductName: <input type="text" name="productName"/>
<br><br>
ProductDesc: <input type="text" name="productDesc"/>
<br><br>
ProductPrice: <input type="text" name="productPrice" />
<br><br>
<input type="submit" value="Submit"/>
<br><br>
</form>
</body>
</html>
實現總結:
1). 搭建 Struts2 的開發環境
2). 不需要顯式的定義 Filter, 而使用的是 struts2 的配置文件.
3). details.jsp 比先前變得簡單了.
${requestScope.product.productName} -> ${productName}
4). 步驟:
I. 由 product-input.action 轉到 /WEB-INF/pages/input.jsp
在 struts2 中配置一個 action
/WEB-INF/pages/input.jsp
II. 由 input.jsp 頁面的 action: product-save.action 到 Product’s save, 再到 /WEB-INF/pages/details.jsp
<action name="product-save" class="com.atguigu.struts2.helloworld.Product"
method="save">
<result name="details">/WEB-INF/pages/details.jsp</result>
</action>
在 Prodcut 中定義一個 save 方法, 且返回值爲 details
struts2.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>
<!--
package: 包. struts2 使用 package 來組織模塊.
name 屬性: 必須. 用於其它的包應用當前包.
extends: 當前包繼承哪個包, 繼承的, 即可以繼承其中的所有的配置. 通常情況下繼承 struts-default
struts-default 這個包在 struts-default.xml 文件中定義.
namespace 可選, 如果它沒有給出, 則以 / 爲默認值.
若 namespace 有一個非默認值, 則要想調用這個包裏的Action,
就必須把這個屬性所定義的命名空間添加到有關的 URI 字符串裏
http://localhost:8080/contextPath/namespace/actionName.action
-->
<package name="helloWorld" extends="struts-default">
<!--
配置一個 action: 一個 struts2 的請求就是一個 action
name: 對應一個 struts2 的請求的名字(或對一個 servletPath, 但去除 / 和擴展名), 不包含擴展名
class 的默認值爲: com.opensymphony.xwork2.ActionSupport
method 的默認值爲: execute
result: 結果.
-->
<action name="product-input"
class="com.opensymphony.xwork2.ActionSupport"
method="execute">
<!--
result: 結果. 表示 action 方法執行後可能返回的一個結果. 所以一個 action 節點可能會有多個 result 子節點.
多個 result 子節點使用 name 來區分
name: 標識一個 result. 和 action 方法的返回值對應. 默認值爲 success
type: 表示結果的類型. 默認值爲 dispatcher(轉發到結果.)
-->
<result name="success" type="dispatcher">/WEB-INF/pages/input.jsp</result>
</action>
<action name="product-save" class="com.atguigu.struts2.helloworld.Product"
method="save">
<result name="details">/WEB-INF/pages/details.jsp</result>
</action>
<action name="test" class="com.atguigu.struts2.helloworld.Product" method="test">
<result>/index.jsp</result>
</action>
</package>
</struts>