- 人人都玩開心網:Ext JS+Android+SSH整合開發Web與移動SNS
- 李寧編著
- 2727字
- 2018-12-27 18:57:24
2.1 MVC框架的王者:Struts 2
Struts 2是當今最吸引眼球,也是使用最廣泛的基于Java的MVC框架。該框架比其前身Struts 1.x更強大,也更容易使用。雖然Struts 2是Struts 1.x的升級版,但在技術實現上幾乎和Struts 1.x一點關系都沒有。實際上,Struts 2是從Webwork發展而來的。Struts 2借用了Struts的知名度和Webwork的優良的設計和理念,成為新的MVC框架的王者。
2.1.1 Struts 2與MVC模式
MVC這個詞也許很多讀者并不陌生,可能耳朵都聽得磨出繭子來了。MVC實際上是三個英文單詞的首字母的組合。M表示Model(模型), V表示View(視圖), C表示Controller (控制器)。基于MVC模式的應用程序從邏輯上被分為Model-View-Controller三部分。也可以換句話說,所有從邏輯上可以分成Model-View-Controller三部分的應用程序都是基于MVC模式的(包括B/S和C/S結構的應用程序)。
那么MVC的這三個部分到底有什么作用呢?讓我們先來回顧一下傳統的Java Web程序是如何設計的。在MVC模式受到廣泛關注之前,我們可能會這樣設計Web應用程序。
在工程中建立很多的JSP頁面和Servlet類。Web程序的用戶接口主要是JSP頁面,那么網頁設計人員就用各種工具設計了很漂亮的JSP頁面(有可能是HTML頁面,然后由開發人員將這些頁面轉成JSP頁面)。下一步,也是最核心的一步,就是為系統添加業務邏輯。開發人員可能會在JSP中添加大量的JSTL標簽以及Java代碼來處理需要動態生成的頁面元素。當JSP頁面需要提交請求時,會提交給另一個JSP頁面,也可能會提交給Servlet。然后在這些JSP頁面或Servlet中處理相應的業務邏輯(包括訪問數據庫等操作),最后返回給JSP頁面,并以用戶期望的格式顯示處理結果。
從上面的處理過程來看,并沒有什么不妥。但如果要發生下面的情況應該怎么辦呢?
當JSP頁面的顯示風格有變化時,可能會修改原來的代碼。例如,在JSP頁面中原來是以列表的方式顯示處理結果的,但現在要以樹形結構顯示處理結果。在這種情況下,修改JSP頁面是必然的,但服務端程序(JSP頁面或Servlet)返回的數據格式有可能并不符合JSP頁面的要求,或者需要對返回的數據進一步加工才能滿足需求。這時如果按傳統的系統結構,就需要修改原來的JSP頁面或Servlet中的代碼。從技術上看,這么做沒有任何問題。但這也違背了設計模式的一個重要原則:對修改關閉,對擴展開放。也就是在添加或修改系統的功能時,應盡量采用擴展的方式,而不是修改原來的代碼。這樣做可以大大避免由于修改原來的代碼而引發的連鎖反應。
那么如果不修改服務端程序的代碼,應該如何做呢?聰明的程序員也許會想到另外一招。就是在服務端再添加一個Servlet(當然也可以使用JSP頁面,不過處理客戶端請求最好用Servlet),這個Servlet作為JSP頁面和處理業務邏輯的Servlet的橋梁。也就是說,JSP頁面并不直接將信息提交給處理業務邏輯的Servlet,而是提交給剛才新添加的Servlet。而由這個新添加的Servlet再訪問處理業務邏輯的Servlet。當需要對返回數據進行二次加工時,并不需要修改那個處理業務邏輯的Servlet,而只需要修改那個中間的Servlet的代碼即可。由于這個Servlet并不處理任何業務邏輯,而只是對返回的數據進行加工,因此,可以完全重寫這個Servlet。
對于JSP頁面來說,可以只向這個中間的Servlet提交信息。當服務端需要改變處理業務邏輯的模塊時,只需要修改這個中間的Servlet即可。而這一步對JSP頁面是完全透明的。從這一點可以看出,這個中間的Servlet起到了一個控制的作用。對于客戶端來說,可以控制服務端返回的數據。對于服務端來說,可以決定將客戶端提交過來的請求交給哪個服務端程序來處理。那么我們就可以將這個中間的Servlet看做是一個Controller(控制器)。這種模式也就是MVC模式。其中M可以看做處理業務邏輯的Servlet, V可以看做是采集用戶數據的JSP頁面,而C則可以看做是這個中間的Servlet。
在Struts 2中也采用了類似的方式,只是并不是用Servlet來實現的。在Struts 2中采用了過濾器的方式來截獲客戶端的請求,并根據請求來調用Struts 2中的控制器。Struts 2中的控制器也被稱為Action對象。Action對象可以是任何類的對象實例,包括POJO類。而Struts 2 MVC中的M也并不是Servlet,而是一個或多個處理業務邏輯的JavaBean的對象實例。讀者在2.1.3節將會看到MVC模式在Struts 2中的真實應用。
2.1.2 Struts 2最新版的下載與安裝
在筆者寫本書時,Struts 2的最新版本是Struts 2.1.6。讀者可以從下面的網址下載Struts 2的最新版本:
在下載完Struts 2的壓縮包后,將其解壓。在lib目錄中包含了當前Struts 2發行版所帶的所有jar文件。但基本的Struts 2應用程序只需要下面7個jar文件:
· struts2-core-2.1.6.jar
· xwork-2.1.2.jar
· struts2-convention-plugin-2.1.6.jar
· ognl-2.6.11.jar
· freemarker-2.3.13.jar
· commons-logging-1.0.4.jar
· commons-fileupload-1.2.1.jar
在找到上面7個jar文件后,將這些jar文件復制到WEB-INF\lib目錄中,并在WEB-INF\web.xml文件中添加如下的代碼:
<filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAnd ExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping>
2.1.3 通過一個計算加減法的Web程序來體驗MVC模式的好處
在這一節我們來做一個計算加減法的Struts 2應用程序。其中加法和減法分別由兩個模型(Model)類完成。這兩個模型類由Action類來調用。當JSP頁面采集了兩個操作數后,會將數據提交給Action,然后Action根據不同的需求調用加法或減法模型類來計算業務邏輯,并對處理結果進行二次加工。
1 編寫計算加法的模型類。
Addition類負責計算兩個操作數的加法,代碼如下:
package net.blogjava.nokiaguy.models; public class Addition { public int add(int x, int y) { return x + y;
} }
2 編寫計算減法的模型類。
Subtraction類負責計算兩個操作數的減法,代碼如下:
package net.blogjava.nokiaguy.models; public class Subtraction { public int sub(int x, int y) { return x - y; } }
3 編寫CalcAction類。
CalcAction類是一個調用Addition和Subtraction類的Action類,代碼如下:
package net.blogjava.nokiaguy.actions; import net.blogjava.nokiaguy.models.Addition; import net.blogjava.nokiaguy.models.Subtraction; public class CalcAction { // 獲得客戶端提交的兩個操作數的值 private int operand1; private int operand2; // 向客戶端返回結果Action類加工過的處理結果 private String result; . // 此處省略了屬性的getter和setter方法 // 處理業務邏輯的方法 public String execute() { Addition addition = new Addition(); // 調用add方法執行業務邏輯 int value = addition.add(operand1, operand2); // 加工處理結果 result = operand1 +"+" + operand2 + "=" + value; return "success"; } }
從CalcAction類的代碼可以看出,在execute方法中只調用了Addition類。如果需要調用Subtraction類,可以修改execute方法,或將該方法名改成execute1,并另外寫一個execute方法來調用Subtraction。由于JSP頁面只向CalcAction類提交請求,而且只從result屬性中獲得處理結果。因此,服務端的業務邏輯切換對于客戶端(JSP頁面)是透明的。
4 配置struts.xml文件。
struts.xml文件是Struts 2中的核心文件。在src目錄中建立一個struts.xml文件,并輸入如下的內容:
<? xml version="1.0" encoding="UTF-8" ? > <! DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="struts2" namespace="/" extends="struts-default"> <! -- 配置CalcAction類 --> <action name="calc" class="net.blogjava.nokiaguy.actions.CalcAction"> <result name="success">/WEB-INF/calc.jsp </result> </action> <! -- 使用通配符為所有在WEB-INF目錄中的JSP頁面指定一個對應的Action --> <action name="*_jsp"> <result>/WEB-INF/{1}.jsp </result> </action> </package> </struts>
由于本例中所有的JSP頁面都放在了WEB-INF目錄中,而且該目錄中的資源不允許直接被客戶端訪問,因此,在這里需要使用通配符為每一個位于WEB-INF目錄中的JSP頁面指定一個對應的Action。比如在WEB-INF目錄中有一個calc.jsp頁面,那么可以通過如下的URL來訪問這個JSP頁面:
http://localhost:8080/sshregister/calc_jsp.action
5 編寫calc.jsp頁面。
calc.jsp頁面有兩個功能:采集兩個操作數和顯示處理結果。在WEB-INF目錄中建立一個calc.jsp頁面,并輸入如下的代碼:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <! -- 引用Struts 2的標簽庫 --> <%@ taglib prefix="s" uri="/struts-tags"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>計算器</title> </head> <body> <! -- 顯示處理結果 --> <s:property value="result"/> <! -- 顯示采集兩個操作數的表單,并向calc提交請求 --> <s:form action="calc"> <s:textfield label="操作數1" name="operand1" /> <s:textfield label="操作數2" name="operand2" /> <s:submit value="計算"/> </s:form>
</body> </html>
到現在為止,這個例子已經編寫完成了,啟動Tomcat后,在瀏覽器地址欄中輸入如下的URL:
http://localhost:8080/sshregister/calc_jsp.action在頁面中的兩個文本框中分別輸入43和56,單擊【計算】按鈕,將會顯示如圖2.1所示的頁面。

圖2.1 計算加法
如果將CalcAction類中的execute方法名改成execute1,并加入如下的execute方法:
public String execute() { Subtraction subtraction = new Subtraction(); // 調用sub方法執行業務邏輯 int value = subtraction.sub(operand1, operand2); // 加工處理結果 result = operand1 +"-" + operand2 + "=" + value; return "success"; }
這時再單擊圖2.1所示頁面上的【計算】按鈕,則會顯示如圖2.2所示的頁面。

圖2.2 計算減法
從上面的測試過程可以看出,JSP頁面和模型類的代碼都未修改,而只修改的Action類(Controller)就可以改變JSP頁面的顯示結果,而且更改了服務端的業務邏輯。