隨筆 - 3, 文章 - 152, 評(píng)論 - 17, 引用 - 0
          數(shù)據(jù)加載中……

          Spring學(xué)習(xí)筆記

          *******************************************************

          作者:陳剛,程序員,廣西省桂林人,廣西師范大學(xué)數(shù)學(xué)系97屆畢業(yè)。

          blog:http://blog.csdn.net/glchengang

          Email:glchengang@yeah.net 

          ******************************************************* 

          1、概念理解

          對(duì)Spring耳聞已久,但一直沒(méi)有時(shí)間和心情去看它,最近它的聲音是越來(lái)越大了,Java視線http://forum.javaeye.com/有不高手在談?wù)撍S谑浅弥锌臻e時(shí)間,我也花了兩個(gè)晚上看了看Spring,看的是夏昕的<Spring開(kāi)發(fā)指南>http://www.xiaxin.net/Spring_Dev_Guide.rar,文章寫得不錯(cuò)。以下談?wù)勎业膶W(xué)習(xí)感受

          一、Spring的IoC(Inversion of Control)。
          這是Spring中得有特點(diǎn)的一部份。IoC又被翻譯成“控制反轉(zhuǎn)”,也不知道是誰(shuí)翻譯得這么別扭,感覺(jué)很深?yuàn)W的詞。其實(shí),原理很簡(jiǎn)單,用一句通俗的話來(lái)說(shuō):就是用XML來(lái)定義生成的對(duì)象。IoC其實(shí)是一種設(shè)計(jì)模式,Spring只是實(shí)現(xiàn)了這種設(shè)計(jì)模式。

          這種設(shè)計(jì)模式是怎么來(lái)的呢?是實(shí)踐中逐漸形成的。

          第一階段:用普通的無(wú)模式來(lái)寫Java程序。一般初學(xué)者都要經(jīng)過(guò)這個(gè)階段。
          第二階段:頻繁的開(kāi)始使用接口,這時(shí),接口一般都會(huì)伴隨著使用工廠模式。
          第三階段:使用IoC模式。工廠模式還不夠好:(1)因?yàn)榈念惖纳纱a寫死在程序里,如果你要換一個(gè)子類,就要修改工廠方法。(2)一個(gè)接口常常意味著一個(gè)生成工廠,會(huì)多出很多工廠類。
              可以把IoC模式看做是工廠模式的升華,可以把IoC看作是一個(gè)大工廠,只不過(guò)這個(gè)大工廠里要生成的對(duì)象都是在XML文件中給出定義的,然后利用Java的“反射”編程,根據(jù)XML中給出的類名生成相應(yīng)的對(duì)象。從實(shí)現(xiàn)來(lái)看,IoC是把以前在工廠方法里寫死的對(duì)象生成代碼,改變?yōu)橛蒟ML文件來(lái)定義,也就是把工廠和對(duì)象生成這兩者獨(dú)立分隔開(kāi)來(lái),目的就是提高靈活性和可維護(hù)性。

              IoC中最基本的Java技術(shù)就是“反射”編程。反射又是一個(gè)生澀的名詞,通俗的說(shuō)反射就是根據(jù)給出的類名(字符串)來(lái)生成對(duì)象。這種編程方式可以讓對(duì)象在生成時(shí)才決定要生成哪一種對(duì)象。我在最近的一個(gè)項(xiàng)目也用到了反射,當(dāng)時(shí)是給出一個(gè).properties文本文件,里面寫了一些全類名(包名+類名),然后,要根據(jù)這些全類名在程序中生成它們的對(duì)象。反射的應(yīng)用是很廣泛的,象Hibernate、String中都是用“反射”做為最基本的技術(shù)手段。

              在過(guò)去,反射編程方式相對(duì)于正常的對(duì)象生成方式要慢10幾倍,這也許也是當(dāng)時(shí)為什么反射技術(shù)沒(méi)有普通應(yīng)用開(kāi)來(lái)的原因。但經(jīng)SUN改良優(yōu)化后,反射方式生成對(duì)象和通常對(duì)象生成方式,速度已經(jīng)相差不大了(但依然有一倍以上的差距)。


              所以要理解IoC,你必須先了解工廠模式和反射編程,否則對(duì)它產(chǎn)生的前因后果和實(shí)現(xiàn)原理都是無(wú)法理解透徹的。只要你理解了這一點(diǎn),你自己也完全可以自己在程序中實(shí)現(xiàn)一個(gè)IoC框架,只不是這還要涉及到XML解析等其他知識(shí),稍微麻煩一些。


              IoC最大的好處是什么?因?yàn)榘褜?duì)象生成放在了XML里定義,所以當(dāng)我們需要換一個(gè)實(shí)現(xiàn)子類將會(huì)變成很簡(jiǎn)單(一般這樣的對(duì)象都是現(xiàn)實(shí)于某種接口的),只要修改XML就可以了,這樣我們甚至可以實(shí)現(xiàn)對(duì)象的熱插撥(有點(diǎn)象USB接口和SCIS硬盤了)。

              IoC最大的缺點(diǎn)是什么?(1)生成一個(gè)對(duì)象的步驟變復(fù)雜了(其實(shí)上操作上還是挺簡(jiǎn)單的),對(duì)于不習(xí)慣這種方式的人,會(huì)覺(jué)得有些別扭和不直觀。(2)對(duì)象生成因?yàn)槭鞘褂梅瓷渚幊蹋谛噬嫌行p耗。但相對(duì)于IoC提高的維護(hù)性和靈活性來(lái)說(shuō),這點(diǎn)損耗是微不足道的,除非某對(duì)象的生成對(duì)效率要求特別高。(3)缺少IDE重構(gòu)操作的支持,如果在Eclipse要對(duì)類改名,那么你還需要去XML文件里手工去改了,這似乎是所有XML方式的缺憾所在。

              總的來(lái)說(shuō)IoC無(wú)論原理和實(shí)現(xiàn)都還算是很簡(jiǎn)單的。一些人曾認(rèn)為IoC沒(méi)什么實(shí)際作用,這種說(shuō)法是可以理解的,因?yàn)槿绻阍诰幊讨泻苌偈褂媒涌冢蚝苌偈褂霉S模式,那么你根本就沒(méi)有使用IoC的強(qiáng)烈需要,也不會(huì)體會(huì)到IoC可貴之處。有些人也說(shuō)要消除工廠模式、單例模式,但是都語(yǔ)焉不詳、人云亦云。但如果你看到IoC模式和用上Spring,那么工廠模式和單例模式的確基本上可以不用了。但它消失了嗎?沒(méi)有!Spring的IoC實(shí)現(xiàn)本身就是一個(gè)大工廠,其中也包含了單例對(duì)象生成方式,只要用一個(gè)設(shè)置就可以讓對(duì)象生成由普通方式變單一實(shí)例方式,非常之簡(jiǎn)單。

             總結(jié):
             (1)IoC原理很簡(jiǎn)單,作用的針對(duì)性也很強(qiáng),不要把它看得很玄乎。
             (2)要理解IoC,首先要了解“工廠、接口、反射”這些概念。


          二、Spring的MVC

          如果你已經(jīng)熟悉Struts,那么不必把MVC做為重點(diǎn)學(xué)習(xí)內(nèi)容。基本上我認(rèn)為Spring  MVC是一個(gè)雞肋,它的技術(shù)上很先進(jìn),但易用性上沒(méi)有Struts好。而且Struts有這么多年的基礎(chǔ)了,Spring很難取代Struts的地位。這就是先入為主的優(yōu)秀,一個(gè)項(xiàng)目經(jīng)理選用一種框架,不能單純的從它的技術(shù)上考慮,還有開(kāi)發(fā)效率,人員配置等都是考慮因素。但做為研究性的學(xué)習(xí),Spring的MVC部份還是蠻有價(jià)值的。


          三、數(shù)據(jù)庫(kù)層的模板
          Spring主要是提供了一些數(shù)據(jù)庫(kù)模板(模板也是一種Java設(shè)計(jì)模式),讓數(shù)據(jù)部分的代碼更簡(jiǎn)潔,那些try...catch都可以不見(jiàn)了。這個(gè)的確是個(gè)好東東。


          四、AOP

          AOP又稱面向方面編程,它的實(shí)現(xiàn)原理還是用了反射:通過(guò)對(duì)某一個(gè)種類的方法名做監(jiān)控來(lái)實(shí)現(xiàn)統(tǒng)一處理。比如:監(jiān)控以“insert”字符串開(kāi)頭的方法名,在這種方法執(zhí)行的前后進(jìn)行某種處理(數(shù)據(jù)庫(kù)事務(wù)等)。但這里我有一個(gè)疑問(wèn)?不一定所有以insert開(kāi)頭的方法都是數(shù)據(jù)庫(kù)操作,哪么當(dāng)某個(gè)insert開(kāi)頭的方法不是數(shù)據(jù)庫(kù)操作,你又對(duì)它進(jìn)行了數(shù)據(jù)事務(wù)的操作,這樣的錯(cuò)誤如何防止???我對(duì)這方面了解不深,還是只知道一個(gè)大概。


          曾看過(guò)一個(gè)程序員發(fā)出這樣的感慨:“框架一個(gè)接一個(gè),學(xué)也學(xué)不完,而且有必要嗎?這樣一層層的加上框架,還不如直接寫JSP來(lái)得直接,效率還高”。我想這種困惑很多人都有吧?但如果你經(jīng)過(guò)的項(xiàng)目漸多,就會(huì)發(fā)現(xiàn),維護(hù)項(xiàng)目要比開(kāi)發(fā)項(xiàng)目更艱難,代價(jià)更大。那種用JSP直接來(lái)寫,層次又不清楚的開(kāi)發(fā),往往最后得到一個(gè)不可再修改的軟件,一團(tuán)亂麻,移一發(fā)而動(dòng)全身。但軟件不象電視機(jī),做好了就不會(huì)改動(dòng)了,軟件是一個(gè)變化的事物,用戶的需求隨時(shí)會(huì)改變,這時(shí)你會(huì)體會(huì)到分層和使用框架的好處了,它們?yōu)槟阕隽塑浖泻芏嗪蜆I(yè)務(wù)無(wú)關(guān)的工作,你可以只關(guān)注業(yè)務(wù),并減少代碼量。唯一缺點(diǎn)就是有一個(gè)學(xué)習(xí)的代價(jià),框架配置上也較麻煩。


          學(xué)習(xí)框架,我認(rèn)為應(yīng)該:第一步,了解這個(gè)框架中的一些關(guān)鍵概念,它的具體含義是什么。第二步,了解這個(gè)框架的精華在哪里,它能對(duì)開(kāi)發(fā)起到什么樣的作用,最好能對(duì)它的原理有一定的了解。第三步,用這個(gè)框架來(lái)寫幾個(gè)例子,實(shí)際體會(huì)一下。我現(xiàn)在還是剛剛大概完成了前兩步,這幾天會(huì)再看看Spring的文檔并用Spring寫幾個(gè)例子,到時(shí)一起發(fā)出來(lái)。

          另外,很贊賞<Spring開(kāi)發(fā)指南>的作者夏昕的觀點(diǎn),將自已的經(jīng)驗(yàn)寫成文檔公開(kāi)出來(lái),不過(guò)一個(gè)人的力量終究太弱。最好能夠形成一個(gè)組織,對(duì)一種新技術(shù),由一兩個(gè)人出一個(gè)大綱,大家把它分了,更寫一章,然后由兩三個(gè)人總集起。我個(gè)人感覺(jué),由于英文語(yǔ)言的關(guān)系,新技術(shù)引進(jìn)到國(guó)內(nèi)的還是太慢了,至少要比國(guó)外慢上一年以上,成立一個(gè)開(kāi)源文檔組織還是很有意義的事。

          第一章  Spring的下載和安裝

          下載主頁(yè)http://www.springframework.org/download.html ,或者直接使用鏈接地址:http://voxel.dl.sourceforge.net/sourceforge/springframework/spring-framework-1.1.4-with-dependencies.zip

          Spring的下載包有兩種:spring-framework-1.1.4-with-dependencies.zipspring-framework-1.1.4.zip,上面的第二個(gè)鏈接就是下載前者,建議你也下載前者,因?yàn)榍罢弑群笳叨嗔艘恍?/SPAN>Spring要用到的第三方包,如hibernatej2eedom4jaopalliancejakarta-commons等。下載包名稱的dependencies就是“依賴”的意思。

          1、解壓后的目錄結(jié)構(gòu)如下:

           

          目錄說(shuō)明:

          l           dist  Spring自已的核心庫(kù)

          l           docs    有一些文檔。

          l           lib   是一些用到的第三方庫(kù)。

          l           mock  仿制品?????????????我也不知道

          l           samples 一些項(xiàng)目例子

          l           src  Spring的源代碼

          l           test  測(cè)試用例

           

          2、新建一個(gè)Eclipse項(xiàng)目

          1)項(xiàng)目名myspring

           

          2)直接單擊“下一步”,再單擊“完成”

          3)在項(xiàng)目下創(chuàng)建一個(gè)lib目錄

           

          4)將Spring的解壓縮目錄distlib都復(fù)制到這個(gè)lib目錄中,然后前者改名成spring,后者先暫時(shí)不動(dòng)吧,以后用到時(shí)才管它。

           

          3、將spring庫(kù)加入到庫(kù)引用

          spring庫(kù)加入到庫(kù)引用中,有如下兩種方法。

          方法一:?jiǎn)螕簟疤砑?/SPAN>JAR”把spring的核心包加入。

           

          方法二:上面的“方法一”簡(jiǎn)單易行,但如果一個(gè)項(xiàng)目要引入的包種類很多,那么就顯示得較亂。還有一種操作麻煩,但較清晰一些的方法。這種方法是使用Eclipse中的“用戶庫(kù)”的方式,如下圖所示:

           

           

          最后的結(jié)果如下圖所示,然后單擊“確定”


           

          返回上一界面后,再單擊“完成”,得到如下圖所示的效果

           

          最后,項(xiàng)目里的spring包的引用都在一個(gè)目錄下,顯示層次感強(qiáng)很多。

           

          以后如果要引入myspring/lib/lib目錄下的第三方包,也按方法二較好:將第三方包的目錄復(fù)制到myspring/lib下,再參照方法二,將其加入庫(kù)引用中即可

          4、設(shè)置日志包的庫(kù)引用

          jakarta-commonslog4j包主要是做為Spring的運(yùn)行時(shí)輸出log(日志)用,如果不設(shè)置日志包,那么日志就沒(méi)法輸出到控制臺(tái),不利于開(kāi)發(fā)和調(diào)試。設(shè)置方式如下:

          1)就照上面的方法,放myspring/lib/lib目錄下的log4j目錄和jakarta-commons目錄往上移一層到myspring/lib目錄下。最后設(shè)置的結(jié)果如下圖所示,這里我們把log4j移到了others目錄,因?yàn)?/SPAN>log4j就一個(gè)JAR包,專門為它象jakarta-commons創(chuàng)建一個(gè)目錄和用戶庫(kù)太不值了,以后可能還會(huì)有這種引用單個(gè)包的時(shí)候,到時(shí)都放到others目錄里好了。

          2)日志的庫(kù)引用完成之后,還要?jiǎng)?chuàng)建一個(gè)日志的配置文件:log4j.properties,其文件內(nèi)容如下:

          log4j.rootLogger=DEBUG, stdout

          log4j.appender.stdout=org.apache.log4j.ConsoleAppender

          log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

          log4j.appender.stdout.layout.ConversionPattern=%c{1} - %m%n

           

          log4j.properties文件的創(chuàng)建位置在src目錄下,如下圖所示:

           

          如果沒(méi)有設(shè)置日志設(shè)置或設(shè)置不對(duì),在使用控制臺(tái)時(shí)會(huì)出現(xiàn)下面所示的紅字。

          java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory

          第二章  SpringIoC的入門實(shí)例

          Spring的模塊化是很強(qiáng)的,各個(gè)功能模塊都是獨(dú)立的,我們可以選擇的使用。這一章先從SpringIoC開(kāi)始。所謂IoC就是一個(gè)用XML來(lái)定義生成對(duì)象的模式,我們看看如果來(lái)使用的。

          1、數(shù)據(jù)模型。

          1、如下圖所示有三個(gè)類,Human(人類)是接口,Chinese(中國(guó)人)是一個(gè)子類,American(美國(guó)人)是另外一個(gè)子類。

          源代碼如下:

          package cn.com.chengang.spring;

          public interface Human {

                 void eat();

                 void walk();

          }

           

          package cn.com.chengang.spring;

          public class Chinese implements Human {

              /* (非 Javadoc

               * @see cn.com.chengang.spring.Human#eat()

               */

              public void eat() {

                  System.out.println("中國(guó)人對(duì)吃很有一套");

              }

           

              /* (非 Javadoc

               * @see cn.com.chengang.spring.Human#walk()

               */

              public void walk() {

                  System.out.println("中國(guó)人行如飛");

              }

          }

           

          package cn.com.chengang.spring;

          public class American implements Human {

              /* (非 Javadoc

               * @see cn.com.chengang.spring.Human#eat()

               */

              public void eat() {

                  System.out.println("美國(guó)人主要以面包為主");

              }

           

              /* (非 Javadoc

               * @see cn.com.chengang.spring.Human#walk()

               */

              public void walk() {

                  System.out.println("美國(guó)人以車代步,有四肢退化的趨勢(shì)");

              }

          }

           

          2、對(duì)以上對(duì)象采用工廠模式的用法如下

          創(chuàng)建一個(gè)工廠類Factory,如下。這個(gè)工廠類里定義了兩個(gè)字符串常量,所標(biāo)識(shí)不同的人種。getHuman方法根據(jù)傳入?yún)?shù)的字串,來(lái)判斷要生成什么樣的人種。

          package cn.com.chengang.spring;

          public class Factory {

              public final static String CHINESE = "Chinese";

              public final static String AMERICAN = "American";

           

              public Human getHuman(String ethnic) {

                  if (ethnic.equals(CHINESE))

                      return new Chinese();

                  else if (ethnic.equals(AMERICAN))

                      return new American();

                  else

                      throw new IllegalArgumentException("參數(shù)(人種)錯(cuò)誤");

              }

          }

           

          下面是一個(gè)測(cè)試的程序,使用工廠方法來(lái)得到了不同的“人種對(duì)象”,并執(zhí)行相應(yīng)的方法。

          package cn.com.chengang.spring;

          public class ClientTest {

              public static void main(String[] args) {

                  Human human = null;

                  human = new Factory().getHuman(Factory.CHINESE);

                  human.eat();

                  human.walk();

                  human = new Factory().getHuman(Factory.AMERICAN);

                  human.eat();

                  human.walk();

              }

          }

           

          控制臺(tái)的打印結(jié)果如下:

           

          3、采用Spring的IoC的用法如下:

          1、在項(xiàng)目根目錄下創(chuàng)建一個(gè)bean.xml文件

          <?xml version="1.0" encoding="UTF-8"?>

          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

          <beans>

                 <bean id="Chinese" class="cn.com.chengang.spring.Chinese"/>

                 <bean id="American" class="cn.com.chengang.spring.American"/>

          </beans>

          bean.xml的位置如下圖,注意不要看花眼把它看成是lib目錄下的了,它是在myspring目錄下的。

           

          2、修改ClientTest程序如下:

          package cn.com.chengang.spring;

          import org.springframework.context.ApplicationContext;

          import org.springframework.context.support.FileSystemXmlApplicationContext;

          public class ClientTest {

              public final static String CHINESE = "Chinese";

              public final static String AMERICAN = "American";

           

              public static void main(String[] args) {

                  //        Human human = null;

                  //        human = new Factory().getHuman(Factory.CHINESE);

                  //        human.eat();

                  //        human.walk();

                  //        human = new Factory().getHuman(Factory.AMERICAN);

                  //        human.eat();

                  //        human.walk();

           

                  ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");

                  Human human = null;

                  human = (Human) ctx.getBean(CHINESE);

                  human.eat();

                  human.walk();

                  human = (Human) ctx.getBean(AMERICAN);

                  human.eat();

                  human.walk();

              }

          }

          從這個(gè)程序可以看到,ctx就相當(dāng)于原來(lái)的Factory工廠,原來(lái)的Factory就可以刪除掉了。然后又把Factory里的兩個(gè)常量移到了ClientTest類里,整個(gè)程序結(jié)構(gòu)基本一樣。

          再回頭看原來(lái)的bean.xml文件的這一句

          <bean id="Chinese" class="cn.com.chengang.spring.Chinese"/>

          id就是ctx.getBean的參數(shù)值,一個(gè)字符串。class就是一個(gè)類(包名+類名)。然后在ClientTest類里獲得Chinese對(duì)象就是這么一句

          human = (Human) ctx.getBean(CHINESE);

          因?yàn)?/SPAN>getBean方法返回的是Object類型,所以前面要加一個(gè)類型轉(zhuǎn)換。

           

          4、總結(jié)

          1)也許有人說(shuō),IoC和工廠模式不是一樣的作用嗎,用IoC好象還麻煩一點(diǎn)。

                 舉個(gè)例子,如果用戶需求發(fā)生變化,要把Chinese類修改一下。那么前一種工廠模式,就要更改Factory類的方法,并且重新編譯布署。而IoC只需要將class屬性改變一下,并且由于IoC利用了Java反射機(jī)制,這些對(duì)象是動(dòng)態(tài)生成的,這時(shí)我們就可以熱插撥Chinese對(duì)象(不必把原程序停止下來(lái)重新編譯布署)

           

                 2)也許有人說(shuō),即然IoC這么好,那么我把系統(tǒng)所有對(duì)象都用IoC方式來(lái)生成。

                 注意,IoC的靈活性是有代價(jià)的:設(shè)置步驟麻煩、生成對(duì)象的方式不直觀、反射比正常生成對(duì)象在效率上慢一點(diǎn)。因此使用IoC要看有沒(méi)有必要,我認(rèn)為比較通用的判斷方式是:用到工廠模式的地方都可以考慮用IoC模式。

           

                 3)在上面的IoC的方式里,還有一些可以變化的地方。比如,bean.xml不一定要放在項(xiàng)目錄下,也可以放在其他地方,比如cn.com.chengang.spring包里。不過(guò)在使用時(shí)也要變化一下,如下所示:

          new FileSystemXmlApplicationContext("src/cn/com/chengang/spring/bean.xml");

          另外,bean.xml也可以改成其他名字。這樣我們?cè)谙到y(tǒng)中就可以分門別類的設(shè)置不同的bean.xml

           

          4)關(guān)于IoC的低侵入性。

          什么是低侵入性?如果你用過(guò)StrutsEJB就會(huì)發(fā)現(xiàn),要繼承一些接口或類,才能利用它們的框架開(kāi)發(fā)。這樣,系統(tǒng)就被綁定在StrutsEJB上了,對(duì)系統(tǒng)的可移植性產(chǎn)生不利的影響。如果代碼中很少涉及某一個(gè)框架的代碼,那么這個(gè)框架就可以稱做是一個(gè)低侵入性的框架。

          Spring的侵入性很低,Humen.javaChinese.java等幾個(gè)類都不必繼承什么接口或類。但在ClientTest里還是有一些Spring的影子:FileSystemXmlApplicationContext類和ctx.getBean方式等。

          現(xiàn)在,低侵入性似乎也成了判定一個(gè)框架的實(shí)現(xiàn)技術(shù)好壞的標(biāo)準(zhǔn)之一。

           

          5)關(guān)于bean.xml的用法

          bean.xml的用法還有很多,其中內(nèi)容是相當(dāng)豐富的。假設(shè)Chinese類里有一個(gè)humenName屬性(姓名),那么原的bean.xml修改如下。此后生成Chinese對(duì)象時(shí),“陳剛”這個(gè)值將自動(dòng)設(shè)置到Chinese類的humenName屬性中。而且由于singletontrue這時(shí)生成Chinese對(duì)象將采用單例模式,系統(tǒng)僅存在一個(gè)Chinese對(duì)象實(shí)例。

          <?xml version="1.0" encoding="UTF-8"?>

          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

          <beans>

                 <bean id="Chinese" class="cn.com.chengang.spring.Chinese"  singleton="true">

                        <property name="humenName">

                               <value>陳剛</value>

                        </property>

                 </bean>

                 <bean id="American" class="cn.com.chengang.spring.American"/>

          </beans>

           

          關(guān)于bean.xml的其它用法,不再詳細(xì)介紹了,大家自己拿Spring的文檔一看就明白了。

          第三章  IoC中的國(guó)際化(CVS版本:V002

          從這一章開(kāi)始,我將把實(shí)例的項(xiàng)目打開(kāi)一個(gè)CVS版本,不知誰(shuí)能提供一個(gè)FTP空間?

          3.1        前言

          標(biāo)題準(zhǔn)確來(lái)說(shuō)應(yīng)該是“使用Spring中的IoC功能來(lái)實(shí)現(xiàn)我們所開(kāi)發(fā)項(xiàng)目系統(tǒng)的國(guó)際化”,國(guó)際化不是針對(duì)IoC的,而是針對(duì)你開(kāi)發(fā)的整個(gè)系統(tǒng)。

          如果你使用過(guò)Eclipse的國(guó)際化,或者用過(guò)Eclipse的“外部化字符串”向?qū)В?/SPAN>Eclipse主菜單:源代碼->外部化字符串),那么對(duì)Spring提供的國(guó)際化功能應(yīng)該是非常容易理解,兩者基本一樣,或者說(shuō)各種Java程序的國(guó)際化方式都基本一樣。

          先談?wù)?/SPAN>Eclipse國(guó)際化的兩個(gè)組成部分:*.properties的資源文件、獲取資源文件內(nèi)容的Message類。

          Spring則和Eclipse的處理類似:資源文件兩者是一樣的,不同語(yǔ)言的翻譯放在不同的資源文件里,連起名規(guī)則都一樣;EclipseMessage類要自己寫(代碼通用,復(fù)制以前項(xiàng)目的即可,或用Eclipse的向?qū)梢粋€(gè)也行),Spring則已經(jīng)有寫好的Message類,我們?cè)?/SPAN>IoCxml文件里注冊(cè)一下即可使用(也可以實(shí)現(xiàn)SpringMessageSource接口,自己來(lái)寫一個(gè)Message類,代碼并不復(fù)雜,不過(guò)這沒(méi)什么必要,用Spring提供的就行了)。

          無(wú)論是EclipseMessage類,還是Spring的自帶的Message類,或是我們自己寫一個(gè)Message類,都是使用JDKjava.util.ResourceBundle類來(lái)實(shí)現(xiàn)*.properties文件的讀取。

          下面用實(shí)例來(lái)體會(huì)一下,先給出本章完成之后的項(xiàng)目結(jié)構(gòu)的截圖:

          3.2  簡(jiǎn)單實(shí)例

          假設(shè)我們有如下程序,程序的作用是打印出一個(gè)字符串

          package cn.com.chengang.spring;

          public class MessageTest {

              public static void main(String[] args) {

                  String str = "ChenGang";

                  System.out.println(str);

              }

          }

          現(xiàn)在,我們要讓這個(gè)程序能夠根據(jù)使用者的語(yǔ)言情況輸出不同的字符,比如:對(duì)英文使用者輸出“ChenGang”,對(duì)中文使用者輸出“陳剛”,對(duì)臺(tái)灣使用輸出“陳剛”等等。這個(gè)需求的實(shí)現(xiàn)方法如下:

          1、創(chuàng)建一系列的資源文件

          cn.com.chengang.spring包下創(chuàng)建以下文件:

          1messages.properties(默認(rèn):英文),內(nèi)容僅一句,如下

          chengang=Giles

          chengang”是鍵值,Giles是要輸出的英文字符串

          2messages_zh_CN.properties(簡(jiǎn)體中文)

          chengang=\u9648\u521A

          \u9648\u521A”是UNICODE碼,對(duì)應(yīng)的中文是“陳剛”

          3messages_ zh_TW.properties(繁體中文)

          chengang=\u9673\u525B

          \u9673\u525B”對(duì)應(yīng)的中文是“陳剛”

           

          附注:由于中文是要轉(zhuǎn)換成UNICODE碼,在編輯和閱讀上有諸多不便,如果是用EclipseIDE,則有一個(gè)編輯資源文件的插件jinto,下載網(wǎng)址是http://www.guh-software.de/,用它打開(kāi)的資源文件如下圖所示,可以看到三個(gè)資源在一個(gè)界面反映了出來(lái)。

          如果你不用Eclipse,而是用EditplugsJDK的方式來(lái)編程(現(xiàn)在還有這樣的原始人嗎?),你也可以用JDK自帶的native2ascii.exe程序來(lái)將中文字串轉(zhuǎn)成UNICODE碼。Ant中還提供了一個(gè)相應(yīng)的任務(wù):<native2ascii encoding="GBK" src="${src}" dest="${build}"/>,其中GBK是一個(gè)中國(guó)的字符集。

           

          2、修改bean.xml

          Spring自帶的org.springframework.context.support.ResourceBundleMessageSource類注冊(cè)到bean.xml中,這個(gè)類的作用是獲取資源文件的內(nèi)容,注冊(cè)到IoCbean.xml文件中是為了自動(dòng)獲得此類的對(duì)象(Spring做了一些簡(jiǎn)化編程的處理)。

          <?xml version="1.0" encoding="UTF-8"?>

          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

          <beans>

                 <bean id="Chinese" class="cn.com.chengang.spring.Chinese"/>

                 <bean id="American" class="cn.com.chengang.spring.American"/>

                 <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">

                        <property name="basenames">

                               <list>

                                      <value>cn.com.chengang.spring.messages</value>

                               </list>

                        </property>

                 </bean>

          </beans>

          代碼說(shuō)明:

          l           id="messageSource" 的設(shè)置是不變的、必須的。

          l           ResourceBundleMessageSourceSpring的一個(gè)Message類。這里還有一個(gè)選擇,用ReloadableResourceBundleMessageSource類,此類可以提供不用重啟即可重新加載資源文件的特性(前者對(duì)資源文件只加載一次)。對(duì)于那種有熱修改資源文件的需求,后者比較合適,只是后者在效率上有可能有損耗,因?yàn)橹辽僖嘁恍z查資源文件是否改變的代碼(這只是我的猜測(cè),我沒(méi)有仔佃去讀這段的源碼)。

          l           basenames”是不變的、必須的。它是ResourceBundleMessageSource的一個(gè)屬性,在源代碼中的定義是“private String[] basenames;”,可見(jiàn)它是一個(gè)字符串?dāng)?shù)組。

          l           cn.com.chengang.spring.messages”是把資源文件的位置傳入到basenames屬性中。注意:三個(gè)資源文件只需要將共同的主名(紅色字體)傳入:messages.propertiesmessages_zh_CN.propertiesmessages_zh_TW.properties

           

          3、使用。修改MessageTest類,如下

          package cn.com.chengang.spring;

          import org.springframework.context.ApplicationContext;

          import org.springframework.context.support.FileSystemXmlApplicationContext;

          public class MessageTest {

              public static void main(String[] args) {

                  ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");

                  String str = ctx.getMessage("chengang", null, null);

                  System.out.println(str);

              }

          }

          代碼說(shuō)明:

          1main方法里

          l           第一句取得bean.xml文件的配置信息。

          l           第二句從資源文件里得到鍵值chengang對(duì)應(yīng)的字符串。

          l           第三句將字符串打印出來(lái),結(jié)果是打印的是“陳剛”,說(shuō)明讀取的是messages_zh_CN.properties資源文件。

          2ctx.getMessage("chengang", null, null);有三個(gè)參數(shù):

          l           第一個(gè)是資源文件的鍵值;

          l           第二個(gè)是資源文件字符串的參數(shù),由于本字符串沒(méi)有參數(shù),所以用一個(gè)null(后面給出了一個(gè)用到字符串參數(shù)的實(shí)例);

          l           第三個(gè)是一個(gè)java.util. Locale類型的參數(shù)。參數(shù)為null,則表示根據(jù)使用者的語(yǔ)言環(huán)境來(lái)選擇Locale,因?yàn)槲矣玫氖侵形陌娴?/SPAN>windows,所以在取字符串時(shí)它自動(dòng)選擇了messages_zh_CN.properties資源文件。
             
          這其中還有一個(gè)控制點(diǎn)在JVMJVM會(huì)根據(jù)當(dāng)前操作系統(tǒng)的語(yǔ)言環(huán)境進(jìn)行相應(yīng)處理,我們可以通過(guò)在JVM啟動(dòng)參數(shù)中追加“-Duser.language=zh_TW”來(lái)設(shè)定當(dāng)前JVM語(yǔ)言類型,通過(guò)JVM級(jí)的設(shè)定,也可以實(shí)現(xiàn)自動(dòng)切換所使用的資源文件類型。
             
          所以這里面的控制語(yǔ)言的方式有三種:從最低層的操作系統(tǒng)的Locale設(shè)定,到更上一層的JVMLocale設(shè)定,再到程序一級(jí)的Locale設(shè)定。

          3.3  資源文件的其他使用方式:

          package cn.com.chengang.spring;

          import java.util.Locale;

          import org.springframework.context.ApplicationContext;

          import org.springframework.context.support.FileSystemXmlApplicationContext;

          import org.springframework.context.support.ResourceBundleMessageSource;

          public class MessageTest {

              public static void main(String[] args) {

                  ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");

                  String str = ctx.getMessage("chengang", null, null);

                  System.out.println(str); //輸出“陳剛”

                  /*

                   * 使用了messages.properties

                   */

                  str = ctx.getMessage("chengang", null, new Locale(""));

                  System.out.println(str);//輸出“Giles

                  /*

                   * 使用了messages_zh_CN.properties

                   */

                  str = ctx.getMessage("chengang", null, new Locale("zh", "CN"));

                  System.out.println(str);//輸出“陳剛”

                  /*

                   * 使用了messages_zh_TW.properties

                   */

                  str = ctx.getMessage("chengang", null, new Locale("zh", "TW"));

                  System.out.println(str);//輸出“陳剛”

                  /*

                   * 使用了messages_zh_TW.properties,從這里可見(jiàn)資源文件的起名可以很隨意,

                   * 比如我們建立一個(gè)messages_123.properties,在傳參數(shù)時(shí)候就可以這樣:

                   * new Locale("123"),一樣也可以取出messages_123.properties中的值

                   */

                  str = ctx.getMessage("chengang", null, new Locale("zh_TW"));

                  System.out.println(str);//輸出“陳剛”

                  /*

                   * 當(dāng)找不到相應(yīng)的資源文件時(shí),使用了messages_zh_CN.properties

                   */

                  str = ctx.getMessage("chengang", null, new Locale("abcd"));

                  System.out.println(str);//輸出“陳剛”

                   /**

                   * 不通過(guò)IoC注冊(cè),直接使用ResourceBundleMessageSource類的寫法。

                   */

                  ResourceBundleMessageSource s = new ResourceBundleMessageSource();

                  s.setBasename("cn.com.chengang.spring.messages");

                  str = s.getMessage("chengang", null, null);

                  System.out.println(str);//輸出“陳剛”

              }

          }

          代碼說(shuō)明:

          前面說(shuō)過(guò)控制語(yǔ)言的方式有三種:從最低層的操作系統(tǒng)的Locale設(shè)定,到更上一層的JVMLocale設(shè)定,再到程序一級(jí)的Locale設(shè)定。我認(rèn)為最佳的方法是在程序一級(jí)進(jìn)行控制:定義一個(gè)統(tǒng)一的Locale靜態(tài)變量,然后整個(gè)系統(tǒng)中只使用這一個(gè)變量,以后就可以通過(guò)界面操作設(shè)置此Locale變量的值,讓用戶來(lái)選擇他所需的軟件語(yǔ)言。而且我們也可以將此靜態(tài)變量設(shè)成null值,來(lái)自動(dòng)選擇資源文件。

          另外,Locale里也定義了一些常量,我們可以直接使用而不必去new一個(gè)Locale對(duì)象,如:“Locale.ENGLISH”。

           

          3.4  再一個(gè)實(shí)例

          這個(gè)實(shí)例演示了如何使用多個(gè)資源文件,以及如何使用字符串參數(shù)

          1)在cn.com.chengang.spring包下再創(chuàng)建一個(gè)資源文件messagesOther_zh_CN.properties

          chengang.info=\u9648\u521A\uFF0C\u7F51\u540D\uFF1A{0}\uFF0C\u82F1\u6587\u540D\uFF1A{1}\uFF0CBlog\uFF1A{2}

          其中UNICODE字符串對(duì)應(yīng)的中文是:“陳剛,網(wǎng)名:{0},英文名:{1}Blog{2}”,這個(gè)字符串一共有三個(gè)參數(shù)。

          2)修改 bean.xml文件

          因?yàn)?/SPAN>basenames屬性是一個(gè)數(shù)組,當(dāng)然也就可以接收多個(gè)資源文件設(shè)定。具體修改如下面的紅字部份

          <?xml version="1.0" encoding="UTF-8"?>

          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

          <beans>

                 <bean id="Chinese" class="cn.com.chengang.spring.Chinese"/>

                 <bean id="American" class="cn.com.chengang.spring.American"/>

                 <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">

                        <property name="basenames">

                               <list>

                                      <value>cn.com.chengang.spring.messages</value>

                                      <value>cn.com.chengang.spring.messagesOther</value>

                               </list>

                        </property>

                 </bean>

          </beans>

           

          3)修改MessageTest類,加入幾行使用的代碼

                  String[] strArgs = new String[3];

                  strArgs[0]="混北民工";

                  strArgs[1]="Giles";

                  strArgs[2]="http://blog.csdn.net/glchengang";

                  str = ctx.getMessage("chengang.info", strArgs, null);

                  System.out.println(str);

          打印出來(lái)的結(jié)果就是:“陳剛,網(wǎng)名:混北民工,英文名:GilesBloghttp://blog.csdn.net/glchengang

           

          3.5  國(guó)際化的實(shí)踐建議

          l           建議一個(gè)包對(duì)應(yīng)一個(gè)資源文件。不要整個(gè)系統(tǒng)都使用一個(gè)資源文件來(lái)翻譯,這樣單個(gè)文檔的體積就太大了,不利于維護(hù);當(dāng)然,也不必一個(gè)類對(duì)應(yīng)一個(gè)資源文件,這樣資源文件又太多了。

          l           建議資源文件和其翻譯類/包在同一目錄下。不過(guò),如果是要將軟件打成一外JAR包或WAR包,建議把資源文件分離出來(lái),這樣可以修改資源文件,而不必再次打包。

          l           建議字符串項(xiàng)的鍵值上加上其所在的類名。比如:上面的chengangchengang.info最好是取名成MessageTest.chengangMessageTest.chengang.info。這樣查找使用此鍵值的類會(huì)方便很多。

           

           

          參考文獻(xiàn)

          l           夏昕的<<Spring開(kāi)發(fā)指南>>  http://www.xiaxin.net/Spring_Dev_Guide.rar 

          posted on 2005-03-05 14:20 閱讀(2087) 評(píng)論(2)  編輯  收藏 所屬分類: J2ee

          評(píng)論

          # re: Spring學(xué)習(xí)筆記[未登錄](méi)  回復(fù)  更多評(píng)論   

          如果只是少數(shù)幾個(gè)開(kāi)發(fā)的話,或是自己開(kāi)發(fā)的話,springmvc+spring 比strut +spring 光配置使用上就要簡(jiǎn)單一倍,而且不存在整合問(wèn)題。比struts要自由了許多,不需要局限于FormBean,sturts標(biāo)簽,可以用任何你喜歡的方式來(lái)實(shí)現(xiàn)。spingmvc 能做的遠(yuǎn)比文檔上寫的要多的多, 慢慢的深入研究可以把它變成屬于你自己的mvc。
          2008-10-16 17:54 | alex

          # re: Spring學(xué)習(xí)筆記  回復(fù)  更多評(píng)論   

          spring代碼下載:http://www.zuidaima.com/share/search.htm?key=spring
          2014-06-18 22:52 | zuidaima
          主站蜘蛛池模板: 济阳县| 即墨市| 大连市| 通州区| 大足县| 明水县| 南江县| 正定县| 泸溪县| 沙雅县| 临安市| 福安市| 吴江市| 赣榆县| 苏州市| 峨边| 泾川县| 清原| 阿瓦提县| 徐州市| 石城县| 鹤岗市| 五台县| 拉萨市| 平安县| 黄梅县| 阳原县| 武安市| 开封市| 晋州市| 婺源县| 肃南| 周宁县| 田东县| 承德市| 临安市| 五莲县| 宾阳县| 凤庆县| 个旧市| 家居|