官术网_书友最值得收藏!

2.1 MVC框架的王者:Struts 2

Struts 2是當(dāng)今最吸引眼球,也是使用最廣泛的基于Java的MVC框架。該框架比其前身Struts 1.x更強(qiáng)大,也更容易使用。雖然Struts 2是Struts 1.x的升級(jí)版,但在技術(shù)實(shí)現(xiàn)上幾乎和Struts 1.x一點(diǎn)關(guān)系都沒(méi)有。實(shí)際上,Struts 2是從Webwork發(fā)展而來(lái)的。Struts 2借用了Struts的知名度和Webwork的優(yōu)良的設(shè)計(jì)和理念,成為新的MVC框架的王者。

2.1.1 Struts 2與MVC模式

MVC這個(gè)詞也許很多讀者并不陌生,可能耳朵都聽(tīng)得磨出繭子來(lái)了。MVC實(shí)際上是三個(gè)英文單詞的首字母的組合。M表示Model(模型), V表示View(視圖), C表示Controller (控制器)。基于MVC模式的應(yīng)用程序從邏輯上被分為Model-View-Controller三部分。也可以換句話說(shuō),所有從邏輯上可以分成Model-View-Controller三部分的應(yīng)用程序都是基于MVC模式的(包括B/S和C/S結(jié)構(gòu)的應(yīng)用程序)。

那么MVC的這三個(gè)部分到底有什么作用呢?讓我們先來(lái)回顧一下傳統(tǒng)的Java Web程序是如何設(shè)計(jì)的。在MVC模式受到廣泛關(guān)注之前,我們可能會(huì)這樣設(shè)計(jì)Web應(yīng)用程序。

在工程中建立很多的JSP頁(yè)面和Servlet類。Web程序的用戶接口主要是JSP頁(yè)面,那么網(wǎng)頁(yè)設(shè)計(jì)人員就用各種工具設(shè)計(jì)了很漂亮的JSP頁(yè)面(有可能是HTML頁(yè)面,然后由開(kāi)發(fā)人員將這些頁(yè)面轉(zhuǎn)成JSP頁(yè)面)。下一步,也是最核心的一步,就是為系統(tǒng)添加業(yè)務(wù)邏輯。開(kāi)發(fā)人員可能會(huì)在JSP中添加大量的JSTL標(biāo)簽以及Java代碼來(lái)處理需要?jiǎng)討B(tài)生成的頁(yè)面元素。當(dāng)JSP頁(yè)面需要提交請(qǐng)求時(shí),會(huì)提交給另一個(gè)JSP頁(yè)面,也可能會(huì)提交給Servlet。然后在這些JSP頁(yè)面或Servlet中處理相應(yīng)的業(yè)務(wù)邏輯(包括訪問(wèn)數(shù)據(jù)庫(kù)等操作),最后返回給JSP頁(yè)面,并以用戶期望的格式顯示處理結(jié)果。

從上面的處理過(guò)程來(lái)看,并沒(méi)有什么不妥。但如果要發(fā)生下面的情況應(yīng)該怎么辦呢?

當(dāng)JSP頁(yè)面的顯示風(fēng)格有變化時(shí),可能會(huì)修改原來(lái)的代碼。例如,在JSP頁(yè)面中原來(lái)是以列表的方式顯示處理結(jié)果的,但現(xiàn)在要以樹形結(jié)構(gòu)顯示處理結(jié)果。在這種情況下,修改JSP頁(yè)面是必然的,但服務(wù)端程序(JSP頁(yè)面或Servlet)返回的數(shù)據(jù)格式有可能并不符合JSP頁(yè)面的要求,或者需要對(duì)返回的數(shù)據(jù)進(jìn)一步加工才能滿足需求。這時(shí)如果按傳統(tǒng)的系統(tǒng)結(jié)構(gòu),就需要修改原來(lái)的JSP頁(yè)面或Servlet中的代碼。從技術(shù)上看,這么做沒(méi)有任何問(wèn)題。但這也違背了設(shè)計(jì)模式的一個(gè)重要原則:對(duì)修改關(guān)閉,對(duì)擴(kuò)展開(kāi)放。也就是在添加或修改系統(tǒng)的功能時(shí),應(yīng)盡量采用擴(kuò)展的方式,而不是修改原來(lái)的代碼。這樣做可以大大避免由于修改原來(lái)的代碼而引發(fā)的連鎖反應(yīng)。

那么如果不修改服務(wù)端程序的代碼,應(yīng)該如何做呢?聰明的程序員也許會(huì)想到另外一招。就是在服務(wù)端再添加一個(gè)Servlet(當(dāng)然也可以使用JSP頁(yè)面,不過(guò)處理客戶端請(qǐng)求最好用Servlet),這個(gè)Servlet作為JSP頁(yè)面和處理業(yè)務(wù)邏輯的Servlet的橋梁。也就是說(shuō),JSP頁(yè)面并不直接將信息提交給處理業(yè)務(wù)邏輯的Servlet,而是提交給剛才新添加的Servlet。而由這個(gè)新添加的Servlet再訪問(wèn)處理業(yè)務(wù)邏輯的Servlet。當(dāng)需要對(duì)返回?cái)?shù)據(jù)進(jìn)行二次加工時(shí),并不需要修改那個(gè)處理業(yè)務(wù)邏輯的Servlet,而只需要修改那個(gè)中間的Servlet的代碼即可。由于這個(gè)Servlet并不處理任何業(yè)務(wù)邏輯,而只是對(duì)返回的數(shù)據(jù)進(jìn)行加工,因此,可以完全重寫這個(gè)Servlet。

對(duì)于JSP頁(yè)面來(lái)說(shuō),可以只向這個(gè)中間的Servlet提交信息。當(dāng)服務(wù)端需要改變處理業(yè)務(wù)邏輯的模塊時(shí),只需要修改這個(gè)中間的Servlet即可。而這一步對(duì)JSP頁(yè)面是完全透明的。從這一點(diǎn)可以看出,這個(gè)中間的Servlet起到了一個(gè)控制的作用。對(duì)于客戶端來(lái)說(shuō),可以控制服務(wù)端返回的數(shù)據(jù)。對(duì)于服務(wù)端來(lái)說(shuō),可以決定將客戶端提交過(guò)來(lái)的請(qǐng)求交給哪個(gè)服務(wù)端程序來(lái)處理。那么我們就可以將這個(gè)中間的Servlet看做是一個(gè)Controller(控制器)。這種模式也就是MVC模式。其中M可以看做處理業(yè)務(wù)邏輯的Servlet, V可以看做是采集用戶數(shù)據(jù)的JSP頁(yè)面,而C則可以看做是這個(gè)中間的Servlet。

在Struts 2中也采用了類似的方式,只是并不是用Servlet來(lái)實(shí)現(xiàn)的。在Struts 2中采用了過(guò)濾器的方式來(lái)截獲客戶端的請(qǐng)求,并根據(jù)請(qǐng)求來(lái)調(diào)用Struts 2中的控制器。Struts 2中的控制器也被稱為Action對(duì)象。Action對(duì)象可以是任何類的對(duì)象實(shí)例,包括POJO類。而Struts 2 MVC中的M也并不是Servlet,而是一個(gè)或多個(gè)處理業(yè)務(wù)邏輯的JavaBean的對(duì)象實(shí)例。讀者在2.1.3節(jié)將會(huì)看到MVC模式在Struts 2中的真實(shí)應(yīng)用。

2.1.2 Struts 2最新版的下載與安裝

在筆者寫本書時(shí),Struts 2的最新版本是Struts 2.1.6。讀者可以從下面的網(wǎng)址下載Struts 2的最新版本:

http://struts.apache.org

在下載完Struts 2的壓縮包后,將其解壓。在lib目錄中包含了當(dāng)前Struts 2發(fā)行版所帶的所有jar文件。但基本的Struts 2應(yīng)用程序只需要下面7個(gè)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個(gè)jar文件后,將這些jar文件復(fù)制到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 通過(guò)一個(gè)計(jì)算加減法的Web程序來(lái)體驗(yàn)MVC模式的好處

在這一節(jié)我們來(lái)做一個(gè)計(jì)算加減法的Struts 2應(yīng)用程序。其中加法和減法分別由兩個(gè)模型(Model)類完成。這兩個(gè)模型類由Action類來(lái)調(diào)用。當(dāng)JSP頁(yè)面采集了兩個(gè)操作數(shù)后,會(huì)將數(shù)據(jù)提交給Action,然后Action根據(jù)不同的需求調(diào)用加法或減法模型類來(lái)計(jì)算業(yè)務(wù)邏輯,并對(duì)處理結(jié)果進(jìn)行二次加工。

1 編寫計(jì)算加法的模型類。

Addition類負(fù)責(zé)計(jì)算兩個(gè)操作數(shù)的加法,代碼如下:

          package net.blogjava.nokiaguy.models;
          public class Addition
          {
              public int add(int x, int y)
              {
              return x + y;
            }
        }

2 編寫計(jì)算減法的模型類。

Subtraction類負(fù)責(zé)計(jì)算兩個(gè)操作數(shù)的減法,代碼如下:

        package net.blogjava.nokiaguy.models;
        public class Subtraction
        {
            public int sub(int x, int y)
            {
            return x - y;
            }
        }

3 編寫CalcAction類。

CalcAction類是一個(gè)調(diào)用Addition和Subtraction類的Action類,代碼如下:

        package net.blogjava.nokiaguy.actions;
        import net.blogjava.nokiaguy.models.Addition;
        import net.blogjava.nokiaguy.models.Subtraction;
        public class CalcAction
        {
            //  獲得客戶端提交的兩個(gè)操作數(shù)的值
            private int operand1;
            private int operand2;
            //  向客戶端返回結(jié)果Action類加工過(guò)的處理結(jié)果
            private String result;
            .
            //  此處省略了屬性的getter和setter方法
            //  處理業(yè)務(wù)邏輯的方法
            public String execute()
            {
                  Addition addition = new Addition();
                //  調(diào)用add方法執(zhí)行業(yè)務(wù)邏輯
                  int value = addition.add(operand1, operand2);
                  //  加工處理結(jié)果
                  result = operand1 +"+" + operand2 + "=" + value;
                  return "success";
            }
        }

從CalcAction類的代碼可以看出,在execute方法中只調(diào)用了Addition類。如果需要調(diào)用Subtraction類,可以修改execute方法,或?qū)⒃摲椒某蒭xecute1,并另外寫一個(gè)execute方法來(lái)調(diào)用Subtraction。由于JSP頁(yè)面只向CalcAction類提交請(qǐng)求,而且只從result屬性中獲得處理結(jié)果。因此,服務(wù)端的業(yè)務(wù)邏輯切換對(duì)于客戶端(JSP頁(yè)面)是透明的。

4 配置struts.xml文件。

struts.xml文件是Struts 2中的核心文件。在src目錄中建立一個(gè)struts.xml文件,并輸入如下的內(nèi)容:

        <? 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頁(yè)面指定一個(gè)對(duì)應(yīng)的Action  -->
                  <action name="*_jsp">
                      <result>/WEB-INF/{1}.jsp
                      </result>
                  </action>
            </package>
        </struts>

由于本例中所有的JSP頁(yè)面都放在了WEB-INF目錄中,而且該目錄中的資源不允許直接被客戶端訪問(wèn),因此,在這里需要使用通配符為每一個(gè)位于WEB-INF目錄中的JSP頁(yè)面指定一個(gè)對(duì)應(yīng)的Action。比如在WEB-INF目錄中有一個(gè)calc.jsp頁(yè)面,那么可以通過(guò)如下的URL來(lái)訪問(wèn)這個(gè)JSP頁(yè)面:

http://localhost:8080/sshregister/calc_jsp.action

5 編寫calc.jsp頁(yè)面。

calc.jsp頁(yè)面有兩個(gè)功能:采集兩個(gè)操作數(shù)和顯示處理結(jié)果。在WEB-INF目錄中建立一個(gè)calc.jsp頁(yè)面,并輸入如下的代碼:

        <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
        <! --  引用Struts 2的標(biāo)簽庫(kù)  -->
        <%@ taglib prefix="s" uri="/struts-tags"%>
        <html>
        <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>計(jì)算器</title>
        </head>
        <body>
        <! --  顯示處理結(jié)果  -->
        <s:property value="result"/>
        <! --  顯示采集兩個(gè)操作數(shù)的表單,并向calc提交請(qǐng)求  -->
        <s:form action="calc">
          <s:textfield  label="操作數(shù)1" name="operand1" />
          <s:textfield  label="操作數(shù)2" name="operand2" />
          <s:submit value="計(jì)算"/>
        </s:form>
        </body>
        </html>

到現(xiàn)在為止,這個(gè)例子已經(jīng)編寫完成了,啟動(dòng)Tomcat后,在瀏覽器地址欄中輸入如下的URL:

http://localhost:8080/sshregister/calc_jsp.action在頁(yè)面中的兩個(gè)文本框中分別輸入43和56,單擊【計(jì)算】按鈕,將會(huì)顯示如圖2.1所示的頁(yè)面。

圖2.1 計(jì)算加法

如果將CalcAction類中的execute方法名改成execute1,并加入如下的execute方法:

          public String execute()
          {
              Subtraction subtraction = new Subtraction();
              //  調(diào)用sub方法執(zhí)行業(yè)務(wù)邏輯
              int value = subtraction.sub(operand1, operand2);
              //  加工處理結(jié)果
              result = operand1 +"-" + operand2 + "=" + value;
              return "success";
          }

這時(shí)再單擊圖2.1所示頁(yè)面上的【計(jì)算】按鈕,則會(huì)顯示如圖2.2所示的頁(yè)面。

圖2.2 計(jì)算減法

從上面的測(cè)試過(guò)程可以看出,JSP頁(yè)面和模型類的代碼都未修改,而只修改的Action類(Controller)就可以改變JSP頁(yè)面的顯示結(jié)果,而且更改了服務(wù)端的業(yè)務(wù)邏輯。

主站蜘蛛池模板: 独山县| 泰和县| 泰兴市| 阿拉善盟| 九龙县| 乐东| 双辽市| 宁都县| 新泰市| 印江| 梨树县| 商都县| 阜城县| 新昌县| 多伦县| 库尔勒市| 灵丘县| 工布江达县| 达拉特旗| 贺兰县| 八宿县| 宜州市| 天门市| 宁津县| 方山县| 彭州市| 晋江市| 北京市| 涪陵区| 吉木萨尔县| 尉氏县| 海门市| 汕头市| 娄烦县| 郎溪县| 灵川县| 堆龙德庆县| 黑龙江省| 罗平县| 曲沃县| 苏尼特右旗|