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

2.2 絕不重造輪子:Spring

設計Spring框架的初衷是簡化Java EE應用的開發過程。但Spring的大多數功能并未提供自己的實現,而是整合了眾多的第三方的技術。比如在持久化功能方面整合了Hibernate、EJB、JDO等技術,在AOP功能方面雖然提供了Spring AOP技術,但仍然可以整合像AspectJ這樣的第三方AOP技術。Spring通過整合大量的第三方框架,大大簡化了這些第三方技術的使用難度。因此,Spring的設計理念就是盡可能地整合第三方的技術,使得這些被整合的技術更容易使用,更易維護,從而大大降低程序開發的難度。除非有必要,否則Spring絕不重新實現這些技術,也就是“絕不重造輪子”。

2.2.1 Spring與IOC模式

裝配JavaBean是Spring框架的核心技術。在傳統的程序中,往往直接使用new關鍵字來創建JavaBean的對象實例,并在程序中為JavaBean的屬性賦值。這樣做就意味著將JavaBean與調用該JavaBean的類牢牢地綁定到一起,也就是說,它們的耦合度太高。如果需要改變設置JavaBean屬性的代碼,那么就必須修改調用JavaBean的源代碼。

雖然修改源代碼也并不是不可以,但理想的實現方法是盡量不修改已經編寫完的源代碼,而采用擴展或修改配置文件的方法改變系統的行為。這也就是“對修改關閉,對擴展開放”的設計原則。為此,Spring提供了一種機制,使得創建JavaBean以及設置JavaBean屬性的工作可以通過配置文件以及Spring框架本身來完成。這樣一來,當某些地方需要改變時,修改Spring的配置文件即可。這個過程實際上就是Spring框架通過讀取相應的配置文件中的內容,并根據這些配置自動裝載(創建JavaBean對象)和配置JavaBean的屬性。因此,也可以稱這個過程為“裝配JavaBean”。

JavaBean的屬性類型有可能是開發人員自定義的類。在這種情況下,Spring建議為這些類提供接口(或抽象類),同時這些類實現這些接口。而JavaBean的相應屬性的類型應該為類所實現的接口,而不應該直接使用類。如下面的代碼所示:

        interface MyInterface
        {
            public void myMethod();
        }
        class MyClass1 implements MyInterface
        {
            public void myMethod(){ ... }
        }
        class MyClass2 implements MyInterface
        {
            public void myMethod(){ ... }
        }
        //  要被Spring裝配的JavaBean
        public class MyJavaBean
        {
            private MyInterface myInterface;
            public MyInterface getMyInterface()
              {
              return myInterface;
            }
            public void setMyInterface(MyInterface myInterface)
            {
                  this.myInterface = myInterface;
            }
        }

從上面的代碼可以看出,MyJavaBean類中的myInterface屬性的數據類型是MyInterface。而在裝配MyJavaBean時,需要為myInterface屬性指定一個實際的類(MyClass1或MyClass2)的對象實例。這樣做的好處很多。例如,如果MyJavaBean類在開發過程中需要對myInterface屬性進測試,這時就可以為myInterface屬性指定一個用于測試的對象實例。當正式發布MyJavaBean類時,需要重新為myInterface屬性指定一個用于發布的對象實例。在這種情況下,就可以編寫兩個實現MyInterface接口的類,一個用于測試(假設為MyClass1),一個在實際發布時使用(假設為MyClass2)。

如果使用Spring框架來裝配MyJavaBean,那么在切換這兩個類時,就不需要修改源代碼了,而只需要修改Spring的配置文件即可。也就是說,MyClass1、MyClass2與MyJavaBean的耦合度非常低,或者說是通過MyInterface接口降低了MyClass1、MyClass2與MyJavaBean的耦合度。當然,也可以將接口換成抽象類,這要根據具體的情況而定。這種通過接口或抽象類降低JavaBean與其他類之間的耦合度的方式也稱為IOC(Inversion of Control,反轉控制)模式。IOC模式也是策略(Strategy)模式的基礎。

注意

在本節及后面的章節會經常提到JavaBean。實際上,JavaBean也是普通的Java類,之所以將某些Java類稱為JavaBean,是因為這些Java類會經常被當成組件使用到。也就是說,可以將被當成組件的經常使用到的Java類稱為JavaBean。作為JavaBean組件,就不可避免地要擁有一些屬性。但Java語言中并沒有定義屬性的語法,因此,JavaBean規范將一個屬性分為getter和setter方法。例如,如果要獲得JavaBean的name屬性的值,就需要調用該屬性的getter方法(getName),如果要為name屬性賦值,就需要調用該屬性的setter方法(setName)。因此,JavaBean的另一個特征就是在JavaBean中都會有一對或多對getter和setter方法。就像上面代碼中的MyJavaBean類一樣,因此,也可以將MyJavaBean類稱為JavaBean。

2.2.2 Spring最新版的下載與安裝

在筆者寫本書時,Spring的最新版本是Spring 2.5.6,讀者可以從如下的地址下載Spring的最新版本:

http://www.springsource.org/download

在下載完Spring的壓縮包后,將其解壓。在<Spring解壓目錄>\dist目錄中有一個spring.jar文件。將該文件復制到WEB-INF\lib目錄中。在2.2.3節將介紹使用Spring的具體方法。

2.2.3 Struts 2和Spring整合的原理

在2.1.3節的例子可以看出。在CalcAction類中直接創建了Addition和Subtraction類的對象實例,并調用了相應的方法來執行業務邏輯。那么在看了2.2.1節介紹的IOC模式后,我們會發現這種調用方式會使CalcAction類和Addition及Subtraction之間的耦合度過高。如果需要將Addition類切換到Subtraction類,或進行相反的切換,就需要修改CalcAction類的代碼。因此,使用Spring來自動創建業務邏輯類的對象實例,并降低它們的耦合度是一個非常不錯的選擇。

最理想的方式就是,當Struts 2創建CalcAction類的對象實例時就自動創建業務邏輯類的對象實例。而在CalcAction類中可以使用業務邏輯類實現的接口來引用相應的對象實例。但是有一個問題,就是如何讓Struts 2在創建CalcAction類的對象實例時就自動創建Additon和Subtraction類的對象實例呢?當然,方法可能比較多。在Struts 2的發行包中提供了一個Struts 2插件,可以非常方便地解決這個問題。這個Struts 2插件的jar文件是struts2-spring-plugin-2.1.6.jar,在使用該插件之前,需要先將該文件復制到WEB-INF\lib目錄中。

在使用Spring之前,需要先在web.xml文件中配置一個監聽器,代碼如下:

        <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListener
        </listener-class>
        </listener>

在完成上面的工作后,需要在CalcAction中定義屬性,例如,定義一個Addition類型的屬性,代碼如下:

        public class CalcAction
        {
            private Additon addition;
            public Addition getAddition()
            {
              return this.addition;
            }
            public void setAddition(Addition addition)
            {
              this.addition = addition;
            }
            …
        }

如果在Spring中的裝配文件(默認為applicationContext.xml)中使用<bean>元素來裝配CalcAction,當id屬性值為addition時,Struts 2會通過上述的插件自動裝配CalcAction,并將裝配后的對象實例賦給CalcAction類的addition屬性。在默認情況下是按屬性名來匹配的,也就是說,當某個<bean>元素的id屬性值與Action類中的某一個屬性名相同時,系統會自動裝配該<bean>元素所指定的JavaBean,并將裝配后的對象賦給Action類的相應屬性(這時該屬性必須有setter方法)。要注意的是,這時<bean>元素的class屬性所指的類必須與Action類中相應屬性的數據類型相同,或是該數據類型(類或接口)的子類。

實際上,自動為Action類相應屬性賦值的功能是通過Struts 2的另外一個autowiring插件完成的。該插件在默認情況下通過將Action類的屬性名和<bean>元素的id屬性值進行匹配的方式,來為Action類的相應屬性賦值。

如果想改變這個默認的設置,例如,使用按類型匹配的方式為Action類的屬性賦值,可以在struts.xml文件中設置autowiring插件的autowireStrategy參數。該參數的值是一個int類型。該參數值從1至4分別對應于Spring的4種自動裝配方式。1表示按名稱裝配;2表示按類型裝配;3表示按構造方法來裝配;4表示以自動的方式裝配。相對應的常量可以查看如下的接口:

        org.springframework.beans.factory.config. AutowireCapableBeanFactory

要想改變autowiring插件的裝配方式,首先要在struts.xml文件中配置一個新的攔截器棧,代碼如下:

        <interceptors>
            <interceptor-stack name="extStack">
              <! --  autowiring未在defaultStack中引用,因此,必須顯示來引用該攔截器  -->
              <interceptor-ref name="autowiring">
                  <! --  設置按類型進行裝配  -->
                  <param name="autowireStrategy">2</param>
              </interceptor-ref>
              <interceptor-ref name="defaultStack" />
            </interceptor-stack>
        </interceptors>
      接下來就可以在相應的<action>元素中引用該攔截器棧了,代碼如下:
        <action name="calc" class="net.blogjava.nokiaguy.actions.CalcAction">
            <interceptor-ref name="extStack" />
            <result name="success">/WEB-INF/springcalc.jsp</result>
        </action>

在進行完上面的設置后,在applicationContext.xml文件中裝配Addition和Subtraction類時,<bean>元素的id值可以任意設置,只要class屬性值符合上述的約定即可。

除了上述的方法可以改變自動裝配方式外,還可以通過在struts.xml文件中設置常量的方式來改變自動裝配的方式。下面的配置代碼將自動裝配方式設置成了按類型自動裝配:

        <constant name="struts.objectFactory.spring.autoWire" value="type"/>

value屬性值除了type外,還可以是name(按名字自動裝配)、constructor(按構造方法自動裝配)、auto(自動偵測方式的自動裝配)。

在2.2.4節會用一個完整的例子來演示如何整合Struts 2和Spring。

2.2.4 用整合Struts 2和Spring的方式重新實現計算加減法的Web程序

在本節將重新實現2.1.3節的計算加減法的Web程序,不過用的是Struts 2和Spring整合的方式,讀者從該例子中可以充分了解到如何通過Spring進行接口解耦合,以及如何方便地切換兩個業務邏輯(Addition和Subtraction類)。

1 編寫Calculator接口

由于本例使用了IOC模式,因此,需要用一個接口作為JavaBean方法的參數類型。Calculator接口的代碼如下:

        package net.blogjava.nokiaguy.models.interfaces;
        public interface Calculator
        {
            public int calc(int x, int y);
            public void setMessage(String s);
            public String getMessage();
        }

其中calc方法是用來計算加法或減法的,而setMessage和getMessage方法用來設置和獲得表示計算結果的字符串。

2 編寫CalculatorMessage類

由于設置和獲得表示計算結果信息的代碼在不同的業務邏輯類中是相同的,因此,需要將這些代碼提出來放在CalculatorMessage類中,然后由業務邏輯類從CalculatorMessage類繼承。CalculatorMessage類的實現代碼如下:

        package net.blogjava.nokiaguy.models;
        import java.text.MessageFormat;
        import net.blogjava.nokiaguy.models.interfaces.Calculator;
        public class CalculatorMessage implements Calculator
        {
            private String msg;
            protected int x, y;
            protected int value;
            //  在calc方法中將兩個操作數(x和y)及計算結果(value)保存在類變量中,
            //  以便getMessage方法可以使用這三個值
            public int calc(int x, int y)
            {
                  this.x = x;
                  this.y = y;
                  return value;
            }
            @Override
            public String getMessage()
            {
                // 使用MessageFormat的format方法為三個占位符賦值,這三個占位符分別是x、y和value
                  return MessageFormat.format(msg, x, y, value);
            }
            @Override
              public void setMessage(String s)
              {
                  msg = s;
              }
          }

3 編寫AdditionCalc和SubtractionCalc類。

為了與2.1.3中的例子進行對比,在本例中新建兩個類來分別完成計算加法和減法的工作。

AdditionCalc類的代碼如下:

        package net.blogjava.nokiaguy.models;
        public class AdditionCalc extends CalculatorMessage
        {
            @Override
            public int calc(int x, int y)
            {
                  value = x + y;
                  return super.calc(x, y);
            }
        }

SubtractionCalc類的代碼如下:

        package net.blogjava.nokiaguy.models;
        import net.blogjava.nokiaguy.models.interfaces.Calculator;
        public class SubtractionCalc extends CalculatorMessage
        {
            @Override
            public int calc(int x, int y)
            {
                  value = x - y;
                  return super.calc(x, y);
            }
        }

4 編寫SpringCalcAction類。

SpringCalcAction是一個Action類,與2.1.3節的CalcAction類的功能類似,只不過利用了Struts 2插件自動裝配Action類屬性的功能來獲得業務邏輯對象。SpringCalcAction類的代碼如下:

        package net.blogjava.nokiaguy.actions;
        import net.blogjava.nokiaguy.models.interfaces.Calculator;
        public class SpringCalcAction
        {
            private int operand1;
            private int operand2;
            //  calculator屬性由Struts 2插件自動裝配,在Spring配置文件中需要有同名的<bean>元素
            private Calculator calculator;
            ... ...
            //  此處省略了屬性的getter和setter方法
            public String execute()
            {
                //  只需要調用Calculator對象的calc方法,在切換業務模型時,不需要修改下面的代碼
                  int value = calculator.calc(operand1, operand2);
              return "success";
            }
        }

5 配置SpringCalcAction類。

SpringCalcAction類的配置代碼如下:

        <action name="springcalc" class="net.blogjava.nokiaguy.actions.SpringCalcAction">
            <! --  <interceptor-ref name="extStack" />-->
            <result name="success">/WEB-INF/springcalc.jsp</result>
        </action>

如果將<interceptor-ref>元素的注釋去掉,Struts 2在創建SpringCalcAction類的對象實例時就會采用類型匹配的方式來裝配SpringCalcAction類中的屬性。extStack攔截器棧的配置見2.2.3節的相關內容。

6 編寫裝配代碼。

Spring插件在默認情況下會讀取WEB-INF\applicationContext.xml文件中的內容。開發人員可以在該文件中配置相應的裝配代碼(標準的XML格式代碼)。當需要裝配的JavaBean太多時,可以在applicationContext.xml文件中使用<import>元素引用其他的裝配文件。也可以在web.xml文件中設置contextCongifLocation參數來指定其他的裝配文件,代碼如下:

        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/calculator.xml, /WEB-INF/applicationContext-*.xml</p
        aram-value>
        </context-param>

如果指定了多個裝配文件,中間用逗號(, )分隔。裝配文件名可以使用通配符,如/WEB-INF/applicationContext-*.xml表示所有以applicationContext開頭的,并且文件擴展名為xml的裝配文件。

本例在applicationContext.xml文件中使用<import>元素來引用其他的裝配文件。在WEB-INF目錄中建立一個applicationContext.xml文件,代碼如下:

        <? xml version="1.0" encoding="UTF-8"? >
        <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://
        www.springframework.org/schema/aop"
            xmlns:tx="http://www.springframework.org/schema/tx"
            xsi:schemaLocation="
                      http://www.springframework.org/schema/beans http://www.springframe
        work.org/schema/beans/spring-beans-2.5.xsd
                    http://www.springframework.org/schema/aop http://www.springframework.
        org/schema/aop/spring-aop-2.5.xsd
                      http://www.springframework.org/schema/tx http://www.springframework.
        org/schema/tx/spring-tx-2.5.xsd">
            <! --  導入用于裝配SpringCalcAction類的相關屬性的裝配文件  -->
            <import resource="calculator.xml"/>
        </beans>

在上面的代碼中使用<import>元素引用了一個calculator.xml文件,在該文件中配置了裝配業務邏輯類的代碼。在WEB-INF目錄中建立一個calculator.xml文件,代碼如下:

        <? xml version="1.0" encoding="UTF-8"? >
        <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www. springframework.org/schema/aop"
            xmlns:tx="http://www.springframework.org/schema/tx"
            xsi:schemaLocation="
                      http://www.springframework.org/schema/beans http://www.springframe
        work.org/schema/beans/spring-beans-2.5.xsd
                    http://www.springframework.org/schema/aop http://www.springframework.
        org/schema/aop/spring-aop-2.5.xsd
                      http://www.springframework.org/schema/tx http://www.springframework.
        org/schema/tx/spring-tx-2.5.xsd">
            <! --  裝配AdditionCalc類  -->
            <bean id="calculator" class="net.blogjava.nokiaguy.models.AdditionCalc">
                  <property name="message">
                      <value>{0}+{1}={2}</value>
                  </property>
            </bean>
        </beans>

在上面的配置代碼中將AdditionCalc類的message屬性設為“{0}+{1}={2}”,其中{n}為占位符,從0開始。{0}表示操作數1; {1}表示操作數2; {2}表示計算結果。在CalculatorMessage類的getMessage方法中使用了MessageFormat類的format方法為這三個占位符賦值。代碼詳見2。如果想切換到SubtractionCalc類,需要將上面代碼中的<bean>元素的id屬性改成其他的值,或將<bean>元素注釋掉,然后再加入如下的裝配代碼:

        <bean id="calculator" class="net.blogjava.nokiaguy.models.SubtractionCalc">
            <property name="message">
              <value>{0}-{1}={2}</value>
            </property>
        </bean>

如果采用了按類型自動裝配的方式,則id屬性的值就變得不重要了,而要想切換業務邏輯類,就需要直接修改class屬性的值了。

7 編寫springcalc.jsp頁面。

springcalc.jsp頁面與calc.jsp頁面類似,但有如下兩點不同:

· <s:form>標簽的action屬性值變為了springcalc。

· 需要使用<s:property value="calculator.message"/>來顯示計算結果。

在測試springcalc.jsp頁面后,會得到與圖2.1和圖2.2相同的效果。

主站蜘蛛池模板: 庆安县| 明星| 桐庐县| 富阳市| 新宾| 通渭县| 长岛县| 华亭县| 平阳县| 开远市| 云浮市| 阿荣旗| 大渡口区| 高唐县| 冀州市| 江口县| 维西| 江陵县| 教育| 泗阳县| 揭东县| 策勒县| 通化县| 峨眉山市| 栾城县| 金华市| 手游| 孝义市| 张北县| 阿荣旗| 竹北市| 酉阳| 明水县| 崇义县| 根河市| 东港市| 台安县| 宜兰县| 紫阳县| 凤翔县| 蓬安县|