<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    shenang博客技術(shù)文檔


    理論不懂就實踐,實踐不會就學理論!

    posts - 35,comments - 55,trackbacks - 0
     

    Spring框架概述

    Ø         主要內(nèi)容:介紹Spring的歷史,Spring的概論和它的體系結(jié)構(gòu),重點闡述它在J2EE中扮演的角色。

    Ø         目 的:全面的了解Spring框架,知道Spring框架所提供的功能,并能將Spring框架和其它框架(WebWork/Struts、hibernate)區(qū)分開來。

     

    Spring是什么?

    Spring是一個開源框架,它由Rod Johnson創(chuàng)建。它是為了解決企業(yè)應用開發(fā)的復雜性而創(chuàng)建的。Spring使用基本的JavaBean來完成以前只可能由EJB完成的事情。然而,Spring的用途不僅限于服務器端的開發(fā)。從簡單性、可測試性和松耦合的角度而言,任何Java應用都可以從Spring中受益。

    ¨         目的:解決企業(yè)應用開發(fā)的復雜性

    ¨         功能:使用基本的JavaBean代替EJB,并提供了更多的企業(yè)應用功能

    ¨         范圍:任何Java應用

    簡單來說,Spring是一個輕量級的控制反轉(zhuǎn)(IoC)和面向切面(AOP)的容器框架。

     

         輕量——從大小與開銷兩方面而言Spring都是輕量的。完整的Spring框架可以在一個大小只有1MB多的JAR文件里發(fā)布。并且Spring所需的處理開銷也是微不足道的。此外,Spring是非侵入式的:典型地,Spring應用中的對象不依賴于Spring的特定類。

         控制反轉(zhuǎn)——Spring通過一種稱作控制反轉(zhuǎn)(IoC)的技術(shù)促進了松耦合。當應用了IoC,一個對象依賴的其它對象會通過被動的方式傳遞進來,而不是這個對象自己創(chuàng)建或者查找依賴對象。你可以認為IoCJNDI相反——不是對象從容器中查找依賴,而是容器在對象初始化時不等對象請求就主動將依賴傳遞給它。

         面向切面——Spring提供了面向切面編程的豐富支持,允許通過分離應用的業(yè)務邏輯與系統(tǒng)級服務(例如審計(auditing)和事務()管理)進行內(nèi)聚性的開發(fā)。應用對象只實現(xiàn)它們應該做的——完成業(yè)務邏輯——僅此而已。它們并不負責(甚至是意識)其它的系統(tǒng)級關(guān)注點,例如日志或事務支持。

         容器——Spring包含并管理應用對象的配置和生命周期,在這個意義上它是一種容器,你可以配置你的每個bean如何被創(chuàng)建——基于一個可配置原型(prototype),你的bean可以創(chuàng)建一個單獨的實例或者每次需要時都生成一個新的實例——以及它們是如何相互關(guān)聯(lián)的。然而,Spring不應該被混同于傳統(tǒng)的重量級的EJB容器,它們經(jīng)常是龐大與笨重的,難以使用。

         框架——Spring可以將簡單的組件配置、組合成為復雜的應用。在Spring中,應用對象被聲明式地組合,典型地是在一個XML文件里。Spring也提供了很多基礎(chǔ)功能(事務管理、持久化框架集成等等),將應用邏輯的開發(fā)留給了你。

    所有Spring的這些特征使你能夠編寫更干凈、更可管理、并且更易于測試的代碼。它們也為Spring中的各種模塊提供了基礎(chǔ)支持。

    Spring的歷史

    Spring的基礎(chǔ)架構(gòu)起源于2000年早期,它是Rod Johnson在一些成功的商業(yè)項目中構(gòu)建的基礎(chǔ)設施。

    2002后期,Rod Johnson發(fā)布了《Expert One-on-One J2EE Design and Development》一書,并隨書提供了一個初步的開發(fā)框架實現(xiàn)——interface21開發(fā)包,interface21就是書中闡述的思想的具體實現(xiàn)。后來,Rod Johnson interface21 開發(fā)包的基礎(chǔ)之上,進行了進一步的改造和擴充,使其發(fā)展為一個更加開放、清晰、全面、高效的開發(fā)框架——Spring。

    20032Spring框架正式成為一個開源項目,并發(fā)布于SourceForge中。

    Spring的使命(Mission Statement

    ¨         J2EE應該更加容易使用。

    ¨         面向?qū)ο蟮脑O計比任何實現(xiàn)技術(shù)(比如J2EE)都重要。

    ¨         面向接口編程,而不是針對類編程。Spring將使用接口的復雜度降低到零。(面向接口編程有哪些復雜度?)

    ¨         代碼應該易于測試。Spring框架會幫助你,使代碼的測試更加簡單。

    ¨         JavaBean提供了應用程序配置的最好方法。

    ¨         Java中,已檢查異常(Checked exception)被過度使用。框架不應該迫使你捕獲不能恢復的異常。

    Spring受到的批判

    Ø         Spring不是一個“標準”。Spring不是J2EE規(guī)范的一部分,沒有通過JCPJava Community Process)的審核認可。

    批判來源于EJB的支持者,他們認為EJB是一個標準,是J2EE規(guī)范的一部分。當然,標準最主要的目的是希望在應用服務器之間是可移植的,可是EJB的移植卻并不輕松,不同應用服務器的ejb部署描述文件總是有著差異。而且EJB開發(fā)的類完全依賴于EJB容器。而Spring對其管理的Bean沒有任何形式的侵入,這樣的Bean是普通Java對象(POJO),那么它就遵循Java標準,可以到處移植。

    Ø         Spring是“超重量級”的。

    Spring涉及的內(nèi)容確實很多(例如:提供了對jdbc、ORM、遠程訪問等等的支持),但其本質(zhì)還是Java技術(shù)的龐大。Spring只是為了這些技術(shù)提供更好的使用方案而已。同時,你可以只選取你需要使用的部分。

    Spring包含的模塊

    Spring框架由七個定義明確的模塊組成(圖1.1)。

    Spring框架概覽圖)

    如果作為一個整體,這些模塊為你提供了開發(fā)企業(yè)應用所需的一切。但你不必將應用完全基于Spring框架。你可以自由地挑選適合你的應用的模塊而忽略其余的模塊。

    就像你所看到的,所有的Spring模塊都是在核心容器之上構(gòu)建的。容器定義了Bean是如何創(chuàng)建、配置和管理的——更多的Spring細節(jié)。當你配置你的應用時,你會潛在地使用這些類。但是作為一名開發(fā)者,你最可能對影響容器所提供的服務的其它模塊感興趣。這些模塊將會為你提供用于構(gòu)建應用服務的框架,例如AOP和持久性。

    核心容器

    這是Spring框架最基礎(chǔ)的部分,它提供了依賴注入(Dependency Injection)特征來實現(xiàn)容器對Bean的管理。這里最基本的概念是BeanFactory,它是任何Spring應用的核心。BeanFactory是工廠模式的一個實現(xiàn),它使用IoC將應用配置和依賴說明從實際的應用代碼中分離出來。

    應用上下文(Context)模塊

    核心模塊的BeanFactory使Spring成為一個容器,而上下文模塊使它成為一個框架。這個模塊擴展了BeanFactory的概念,增加了對國際化(I18N)消息、事件傳播以及驗證的支持。

    另外,這個模塊提供了許多企業(yè)服務,例如電子郵件、JNDI訪問、EJB集成、遠程以及時序調(diào)度(scheduling)服務。也包括了對模版框架例如VelocityFreeMarker集成的支持。

    SpringAOP模塊

        Spring在它的AOP模塊中提供了對面向切面編程的豐富支持。這個模塊是在Spring應用中實現(xiàn)切面編程的基礎(chǔ)。為了確保Spring與其它AOP框架的互用性, SpringAOP支持基于AOP聯(lián)盟定義的API。AOP聯(lián)盟是一個開源項目,它的目標是通過定義一組共同的接口和組件來促進AOP的使用以及不同的AOP實現(xiàn)之間的互用性。通過訪問他們的站點http://aopalliance. sourceforge.net,你可以找到關(guān)于AOP聯(lián)盟的更多內(nèi)容。

    SpringAOP模塊也將元數(shù)據(jù)編程引入了Spring。使用Spring的元數(shù)據(jù)支持,你可以為你的源代碼增加注釋,指示Spring在何處以及如何應用切面函數(shù)。

    JDBC抽象和DAO模塊

    使用JDBC經(jīng)常導致大量的重復代碼,取得連接、創(chuàng)建語句、處理結(jié)果集,然后關(guān)閉連接。SpringJDBCDAO模塊抽取了這些重復代碼,因此你可以保持你的數(shù)據(jù)庫訪問代碼干凈簡潔,并且可以防止因關(guān)閉數(shù)據(jù)庫資源失敗而引起的問題。

    這個模塊還在幾種數(shù)據(jù)庫服務器給出的錯誤消息之上建立了一個有意義的異常層。使你不用再試圖破譯神秘的私有的SQL錯誤消息!

    另外,這個模塊還使用了SpringAOP模塊為Spring應用中的對象提供了事務管理服務。

    對象/關(guān)系映射集成模塊

    對那些更喜歡使用對象/關(guān)系映射工具而不是直接使用JDBC的人,Spring提供了ORM模塊。Spring并不試圖實現(xiàn)它自己的ORM解決方案,而是為幾種流行的ORM框架提供了集成方案,包括Hibernate、JDOiBATIS SQL映射。Spring的事務管理支持這些ORM框架中的每一個也包括JDBC

    SpringWeb模塊

    Web上下文模塊建立于應用上下文模塊之上,提供了一個適合于Web應用的上下文。另外,這個模塊還提供了一些面向服務支持。例如:實現(xiàn)文件上傳的multipart請求,它也提供了Spring和其它Web框架的集成,比如Struts、WebWork。

    SpringMVC框架

    Spring為構(gòu)建Web應用提供了一個功能全面的MVC框架。雖然Spring可以很容易地與其它MVC框架集成,例如Struts,但SpringMVC框架使用IoC對控制邏輯和業(yè)務對象提供了完全的分離。

    它也允許你聲明性地將請求參數(shù)綁定到你的業(yè)務對象中,此外,SpringMVC框架還可以利用Spring的任何其它服務,例如國際化信息與驗證。

    總結(jié)

    Spring帶來了復雜的J2EE開發(fā)的春天。它的核心是輕量級的IoC容器,它的目標是為J2EE應用提供了全方位的整合框架,在Spring框架下實現(xiàn)多個子框架的組合,這些子框架之間可以彼此獨立,也可以使用其它的框架方案加以代替,Spring希望為企業(yè)應用提供一站式(one-stop shop)的解決方案。

    Spring的IoC容器

    Ø         主要內(nèi)容:從最基本的面向接口編程逐步引入IoC設計模式(以銀行卡:Card為例,接口-單例-工廠方法-IoC);詳細介紹IoC的三種實現(xiàn),并對其優(yōu)、缺點進行比較;之后開始引入SpringIoC容器,詳細介紹如何使用SpringIoC容器組織業(yè)務組件。

    Ø         目的:使學員真正理解IoC的概念、優(yōu)點,并掌握Spring IoC容器的使用。

     

    用戶注冊的例子

           我們先看看更進一步的需求:實現(xiàn)一個用戶注冊信息持久化的類。

    功能:

    1、 保存用戶注冊的信息;

    2、 根據(jù)用戶的名稱獲得該注冊用戶。

    雖然功能簡單,但它對持久化方式的要求卻非常的靈活:

    1、 在內(nèi)存中持久化,供測試、演示使用。

    2、 如果用戶的數(shù)據(jù)很少,將用戶信息持據(jù)化到文本文件中。

    3、 如果用戶信息很多,并需要一些靈活的查詢,則需要使用JDBC技術(shù)將用將用戶信息持久化到數(shù)據(jù)庫中。

    4、 面對企業(yè)復雜關(guān)聯(lián)的數(shù)據(jù),甚至需要使用持久層框架來實現(xiàn)用戶信息的持久化,比如:iBATIS、Hibernate等。

    如何去設計、實現(xiàn)我們這個持久化類呢?

    我們遵循軟件開發(fā)的原則“首先讓它跑起來,再去優(yōu)化(重構(gòu))它”,我們首先實現(xiàn)最簡單的在內(nèi)存中持久化用戶信息。

           既然我們要保存和取得用戶信息,首先應該設計用戶類。代碼如下:

    User.java

    public class User {

        private Long id;

        private String name;

        private String password;

        private String group;

       

        public User(String name,String password){

            this.name = name;

            this.password = password;

    }

    //相應的get/set方法

    ………..

    }

    持久化類有兩個方法,分別在內(nèi)存中保存和獲取User對象。代碼如下:

    MemoryUserPersist.java

    public class MemoryUserPersist {

        private static Map users = new HashMap();

        static{

            User defaultAdmin = new User("Moxie","pass");

            users.put(defaultAdmin.getName(),defaultAdmin);

        }

        public MemoryUserPersist (){

           

        }

        public void saveUser(User user){

            users.put(user.getName(),user);

        }

        public User LoadUser(String userName){

            return (User)users.get(userName);

        }

    }

    用戶持久化類完成之后,我們就可以在客戶端UserRegister中使用它了。例如:用戶注冊時,UserRegister代碼片斷如下:

    MemoryUserPersist userPersist = new MemoryUserPersist ();

    userPersist.saveUser(user);

           可是,現(xiàn)在如果要在文本文件中持久化User,又該如何實現(xiàn)呢?實現(xiàn)一個TextUserPersist類,這個并不困難。但客戶端代碼將面臨重大災難:找到所有使用過MemoryUserPersist的客戶端類,將他們中的MemoryUserPersist逐個手工修改為 TextUserPersist,并且重新編譯,當然以前的測試也必須全部從頭來過!

           人生的浩劫只是剛剛開始,因為根據(jù)前面的需求我們至少要分別實現(xiàn)四種持久化方式!這時,你一定和我一樣在期待著救世主的早日降臨——接口(Interface)。

    面向接口編程

    什么是接口?

    ¨         接口定義了行為的協(xié)議,這些行為在繼承接口的類中實現(xiàn)。

    ¨         接口定義了很多方法,但是沒有實現(xiàn)它們。類履行接口協(xié)議并實現(xiàn)所有定義在接口中的方法。

    ¨         接口是一種只有聲明沒有實現(xiàn)的特殊類。

    接口的優(yōu)點:

    ¨         Client不必知道其使用對象的具體所屬類。

    ¨         一個對象可以很容易地被(實現(xiàn)了相同接口的)的另一個對象所替換。

    ¨         對象間的連接不必硬綁定(hardwire)到一個具體類的對象上,因此增加了靈活性。

    ¨         松散藕合(loosens coupling)。

    ¨         增加了重用的可能性。

    接口的缺點:

    設計的復雜性略有增加

    (用戶持久化類)重構(gòu)第一步——面向接口編程

    1、 設計用戶持久化類的接口UserDao,代碼如下:

    public interface UserDao {

        public void save(User user);

        public User load(String name);

    }

    2、 具體的持久化來必須要繼承UserDao接口,并實現(xiàn)它的所有方法。我們還是首先實現(xiàn)內(nèi)存持久化的用戶類:

    public class MemoryUserDao implements UserDao{

        private static Map users = new HashMap();;

        static{

            User user = new User("Moxie","pass");

            users.put(user.getName(),user);

        }

        public void save(User user) {

            users.put(user.getId(),user);

        }

        public User load(String name) {

            return (User)users.get(name);

        }

    }

    MemoryUserDao的實現(xiàn)代碼和上面的MemoryUserPersist基本相同,唯一區(qū)別是MemoryUserDao類繼承了UserDao接口,它的save()load()方法是實現(xiàn)接口的方法。

    這時,客戶端UserRegister的代碼又該如何實現(xiàn)呢?

    UserDao userDao = new MemoryUserDao();

    userDao.save(user);

    (注:面向?qū)ο?#8220;多態(tài)”的闡述)

    如果我們再切換到文本的持久化實現(xiàn)TextUserDao,客戶端代碼仍然需要手工修改。雖然我們已經(jīng)使用了面向?qū)ο蟮亩鄳B(tài)技術(shù),對象userDao方法的執(zhí)行都是針對接口的調(diào)用,但userDao對象的創(chuàng)建卻依賴于具體的實現(xiàn)類,比如上面MemoryUserDao。這樣我們并沒有完全實現(xiàn)前面所說的“Client不必知道其使用對象的具體所屬類”。

    如何解決客戶端對象依賴具體實現(xiàn)類的問題呢?

    下面該是我們的工廠(Factory)模式出場了!

    重構(gòu)第二步——工廠(Factory)模式

    我們使用一個工廠類來實現(xiàn)userDao對象的創(chuàng)建,這樣客戶端只要知道這一個工廠類就可以了,不用依賴任何具體的UserDao實現(xiàn)。創(chuàng)建userDao對象的工廠類UserDaoFactory代碼如下:

    public class UserDaoFactory {

        public static UserDao createUserDao(){

            return new MemoryUserDao();

        }

    }

    客戶端UserRegister代碼片斷如下:

    UserDao userDao = UserDaoFactory. CreateUserDao();

    userDao.save(user);

    現(xiàn)在如果再要更換持久化方式,比如使用文本文件持久化用戶信息。就算有再多的客戶代碼調(diào)用了用戶持久化對象我們都不用擔心了。因為客戶端和用戶持久化對象的具體實現(xiàn)完全解耦。我們唯一要修改的只是一個UserDaoFactory類。

    重構(gòu)第三步——工廠(Factory)模式的改進

    到這里人生的浩劫已經(jīng)得到了拯救。但我們?nèi)圆粷M足,因為假如將內(nèi)存持久化改為文本文件持久化仍然有著硬編碼的存在——UserDaoFactory類的修改。代碼的修改就意味著重新編譯、打包、部署甚至引入新的Bug。所以,我們不滿足,因為它還不夠完美!

    如何才是我們心目中的完美方案?至少要消除更換持久化方式時帶來的硬編碼。具體實現(xiàn)類的可配置不正是我們需要的嗎?我們在一個屬性文件中配置UserDao的實現(xiàn)類,例如:

    在屬性文件中可以這樣配置:userDao = com.test.MemoryUserDao。UserDao的工廠類將從這個屬性文件中取得UserDao實現(xiàn)類的全名,再通過Class.forName(className).newInstance()語句來自動創(chuàng)建一個UserDao接口的具體實例。UserDaoFactory代碼如下:

    public class UserDaoFactory {

        public static UserDao createUserDao(){

            String className = "";

    // ……從屬性文件中取得這個UserDao的實現(xiàn)類全名。

            UserDao userDao = null;

            try {

                userDao = (UserDao)Class.forName(className).newInstance();

            } catch (Exception e) {

                e.printStackTrace();

            }

            return userDao;

       }

    通過對工廠模式的優(yōu)化,我們的方案已近乎完美。如果現(xiàn)在要更換持久化方式,不需要再做任何的手工編碼,只要修改配置文件中的userDao實現(xiàn)類名,將它設置為你需要更換的持久化類名即可。

    我們終于可以松下一口氣了?不,矛盾仍然存在。我們引入了接口,引入了工廠模式,讓我們的系統(tǒng)高度的靈活和可配置,同時也給開發(fā)帶來了一些復雜度:1、本來只有一個實現(xiàn)類,后來卻要為這個實現(xiàn)類引入了一個接口。2、引入了一個接口,卻還需要額外開發(fā)一個對應的工廠類。3、工廠類過多時,管理、維護非常困難。比如:當UserDao的實現(xiàn)類是JdbcUserDao,它使用JDBC技術(shù)來實現(xiàn)用戶信息從持久化。也許要在取得JdbcUserDao實例時傳入數(shù)據(jù)庫Connection,這是仍少UserDaoFactory的硬編碼。

    當然,面接口編程是實現(xiàn)軟件的可維護性和可重用行的重要原則已經(jīng)勿庸置疑。這樣,第一個復雜度問題是無法避免的,再說一個接口的開發(fā)和維護的工作量是微不足道的。但后面兩個復雜度的問題,我們是完全可以解決的:工廠模式的終極方案——IoC模式。

    重構(gòu)第四步-IoC容器

    使用IoC容器,用戶注冊類UserRegister不用主動創(chuàng)建UserDao實現(xiàn)類的實例。由IoC容器主動創(chuàng)建UserDao實現(xiàn)類的實例,并注入到用戶注冊類中。我們下面將使用Spring提供的IoC容器來管理我們的用戶注冊類。

    用戶注冊類UserRegister的部分代碼如下:

    public class UserRegister {

        private UserDao userDao = null;//由容器注入的實例對象

        public void setUserDao(UserDao userDao){

            this.userDao = userDao;

        }

           // UserRegister的業(yè)務方法

    }

    在其它的UserRegister方法中就可以直接使用userDao對象了,它的實例由Spring容器主動為它創(chuàng)建。但是,如何組裝一個UserDao的實現(xiàn)類到UserRegister中呢?哦,Spring提供了配置文件來組裝我們的組件。Spring的配置文件applicationContext.xml代碼片斷如下:

    <bean id="userRegister" class="com.dev.spring.simple.UserRegister">

           <property name="userDao"><ref local="userDao"/></property>

    </bean>

    <bean id="userDao" class="com.dev.spring.simple.MemoryUserDao"/>

    控制反轉(zhuǎn)(IoC/依賴注入(DI

    什么是控制反轉(zhuǎn)/依賴注入?

    控制反轉(zhuǎn)(IoC=Inversion of ControlIoC,用白話來講,就是由容器控制程序之間的(依賴)關(guān)系,而非傳統(tǒng)實現(xiàn)中,由程序代碼直接操控。這也就是所謂“控制反轉(zhuǎn)”的概念所在:(依賴)控制權(quán)由應用代碼中轉(zhuǎn)到了外部容器,控制權(quán)的轉(zhuǎn)移,是所謂反轉(zhuǎn)。

    IoC也稱為好萊塢原則(Hollywood Principle):“Don’t call us, we’ll call you”。即,如果大腕明星想演節(jié)目,不用自己去找好萊塢公司,而是由好萊塢公司主動去找他們(當然,之前這些明星必須要在好萊塢登記過)。

    正在業(yè)界為IoC爭吵不休時,大師級人物Martin Fowler也站出來發(fā)話,以一篇經(jīng)典文章《Inversion of Control Containers and the Dependency Injection pattern》為IoC正名,至此,IoC又獲得了一個新的名字:“依賴注入 Dependency Injection)”。

    相對IoC 而言,“依賴注入”的確更加準確的描述了這種古老而又時興的設計理念。從名字上理解,所謂依賴注入,即組件之間的依賴關(guān)系由容器在運行期決定,形象的來說,即由容器動態(tài)的將某種依賴關(guān)系注入到組件之中。

    例如前面用戶注冊的例子。UserRegister依賴于UserDao的實現(xiàn)類,在最后的改進中我們使用IoC容器在運行期動態(tài)的為UserRegister注入UserDao的實現(xiàn)類。即UserRegisterUserDao的依賴關(guān)系由容器注入,UserRegister不用關(guān)心UserDao的任何具體實現(xiàn)類。如果要更改用戶的持久化方式,只要修改配置文件applicationContext.xm即可。

    依賴注入機制減輕了組件之間的依賴關(guān)系,同時也大大提高了組件的可移植性,這意味著,組件得到重用的機會將會更多。

    依賴注入的三種實現(xiàn)形式

    我們將組件的依賴關(guān)系由容器實現(xiàn),那么容器如何知道一個組件依賴哪些其它的組件呢?例如用戶注冊的例子:容器如何得知UserRegister依賴于UserDao呢。這樣,我們的組件必須提供一系列所謂的回調(diào)方法(這個方法并不是具體的Java類的方法),這些回調(diào)方法會告知容器它所依賴的組件。根據(jù)回調(diào)方法的不同,我們可以將IoC分為三種形式:

    Type1-接口注入(Interface Injection

    它是在一個接口中定義需要注入的信息,并通過接口完成注入。Apache Avalon是一個較為典型的Type1IOC容器,WebWork框架的IoC容器也是Type1型。

    當然,使用接口注入我們首先要定義一個接口,組件的注入將通過這個接口進行。我們還是以用戶注冊為例,我們開發(fā)一個InjectUserDao接口,它的用途是將一個UserDao實例注入到實現(xiàn)該接口的類中。InjectUserDao接口代碼如下:

    public interface InjectUserDao {

        public void setUserDao(UserDao userDao);

    }

    UserRegister需要容器為它注入一個UserDao的實例,則它必須實現(xiàn)InjectUserDao接口。UserRegister部分代碼如下:

    public class UserRegister implements InjectUserDao{

        private UserDao userDao = null;//該對象實例由容器注入

        public void setUserDao(UserDao userDao) {

            this.userDao = userDao;

        }

    // UserRegister的其它業(yè)務方法

    }

    同時,我們需要配置InjectUserDao接口和UserDao的實現(xiàn)類。如果使用WebWork框架則配置文件如下:

    <component>

            <scope>request</scope>

            <class>com.dev.spring.simple.MemoryUserDao</class>

            <enabler>com.dev.spring.simple.InjectUserDao</enabler>

    </component>

    這樣,當IoC容器判斷出UserRegister組件實現(xiàn)了InjectUserDao接口時,它就將MemoryUserDao實例注入到UserRegister組件中。

    Type2-設值方法注入(Setter Injection

    在各種類型的依賴注入模式中,設值注入模式在實際開發(fā)中得到了最廣泛的應用(其中很大一部分得力于Spring框架的影響)。

    基于設置模式的依賴注入機制更加直觀、也更加自然。前面的用戶注冊示例,就是典

    型的設置注入,即通過類的setter方法完成依賴關(guān)系的設置。

    Type3-構(gòu)造子注入(Constructor Injection

    構(gòu)造子注入,即通過構(gòu)造函數(shù)完成依賴關(guān)系的設定。將用戶注冊示例該為構(gòu)造子注入,UserRegister代碼如下:

    public class UserRegister {

        private UserDao userDao = null;//由容器通過構(gòu)造函數(shù)注入的實例對象

        public UserRegister(UserDao userDao){

            this.userDao = userDao;

        }

        //業(yè)務方法

    }

    幾種依賴注入模式的對比總結(jié)

    接口注入模式因為歷史較為悠久,在很多容器中都已經(jīng)得到應用。但由于其在靈活性、易用性上不如

    其他兩種注入模式,因而在IOC的專題世界內(nèi)并不被看好。

    Type2Type3型的依賴注入實現(xiàn)則是目前主流的IOC實現(xiàn)模式。這兩種實現(xiàn)方式各有特點,也各具優(yōu)勢。

    Type2 設值注入的優(yōu)勢

    1 對于習慣了傳統(tǒng)JavaBean開發(fā)的程序員而言,通過setter方法設定依賴關(guān)系顯得更加直觀,更加自然。

    2 如果依賴關(guān)系(或繼承關(guān)系)較為復雜,那么Type3模式的構(gòu)造函數(shù)也會相當龐大(我們需要在構(gòu)造函數(shù)中設定所有依賴關(guān)系),此時Type2模式往往更為簡潔。

    3 對于某些第三方類庫而言,可能要求我們的組件必須提供一個默認的構(gòu)造函數(shù)(如Struts中的Action),此時Type3類型的依賴注入機制就體現(xiàn)出其局限性,難以完成我們期望的功能。

    Type3 構(gòu)造子注入的優(yōu)勢:

    1 “在構(gòu)造期即創(chuàng)建一個完整、合法的對象”,對于這條Java設計原則,Type3無疑是最好的響應者。

    2 避免了繁瑣的setter方法的編寫,所有依賴關(guān)系均在構(gòu)造函數(shù)中設定,依賴關(guān)系集中呈現(xiàn),更加易讀。

    3 由于沒有setter方法,依賴關(guān)系在構(gòu)造時由容器一次性設定,因此組件在被創(chuàng)建之后即處于相對“不變”的穩(wěn)定狀態(tài),無需擔心上層代碼在調(diào)用過程中執(zhí)行setter方法對組件依賴關(guān)系產(chǎn)生破壞,特別是對于Singleton模式的組件而言,這可能對整個系統(tǒng)產(chǎn)生重大的影響。

    4 同樣,由于關(guān)聯(lián)關(guān)系僅在構(gòu)造函數(shù)中表達,只有組件創(chuàng)建者需要關(guān)心組件內(nèi)部的依賴關(guān)系。對調(diào)用者而言,組件中的依賴關(guān)系處于黑盒之中。對上層屏蔽不必要的信息,也為系統(tǒng)的層次清晰性提供了保證。

    5 通過構(gòu)造子注入,意味著我們可以在構(gòu)造函數(shù)中決定依賴關(guān)系的注入順序,對于一個大量依賴外部服務的組件而言,依賴關(guān)系的獲得順序可能非常重要,比如某個依賴關(guān)系注入的先決條件是組件的UserDao及相關(guān)資源已經(jīng)被設定。

    可見,Type3Type2模式各有千秋,而Spring、PicoContainer都對Type3Type2類型的依賴注入機制提供了良好支持。這也就為我們提供了更多的選擇余地。理論上,以Type3類型為主,輔之以Type2類型機制作為補充,可以達到最好的依賴注入效果,不過對于基于Spring Framework開發(fā)的應用而言,Type2使用更加廣泛。

    BeanFactory

    ApplicationContext

    BeanFactorySpring的“心臟”。它就是Spring IoC容器的真面目。Spring使用BeanFactory來實例化、配置和管理Bean。但是,在大多數(shù)情況我們并不直接使用BeanFactory,而是使用ApplicationContext。它也是BeanFactory的一個實現(xiàn),但是它添加了一系列“框架”的特征,比如:國際化支持、資源訪問、事件傳播等。ApplicationContext我們將在后面章節(jié)中介紹。

    BeanFactory

     


    BeanFactory其實是一個接口-org.springframework.beans.factory.BeanFactory,它可以配置和管理幾乎所有的Java類。當然,具體的工作是由實現(xiàn)BeanFactory接口的實現(xiàn)類完成。我們最常用的BeanFactory實現(xiàn)是org.springframework.beans.factory.xml.XmlBeanFactory。它從XML文件中讀取Bean的定義信息。當BeanFactory被創(chuàng)建時,Spring驗證每個Bean的配置。當然,要等Bean創(chuàng)建之后才能設置Bean的屬性。單例(Singleton)Bean在啟動時就會被BeanFactory實例化,其它的Bean在請求時創(chuàng)建。根據(jù)BeanFactoryJava文檔(Javadocs)介紹,“Bean定義的持久化方式?jīng)]有任何的限制:LDAPRDBMS、XML、屬性文件,等等”。現(xiàn)在Spring已提供了XML文件和屬性文件的實現(xiàn)。無疑,XML文件是定義Bean的最佳方式。

    BeanFactory是初始化Bean和調(diào)用它們生命周期方法的“吃苦耐勞者”。注意,BeanFactory只能管理單例(SingletonBean的生命周期。它不能管理原型(prototype,非單例)Bean的生命周期。這是因為原型Bean實例被創(chuàng)建之后便被傳給了客戶端,容器失去了對它們的引用。

    BeanFactory管理Bean(組件)的生命周期

    下圖描述了Bean的生命周期。它是由IoC容器控制。IoC容器定義Bean操作的規(guī)則,即Bean的定義(BeanDefinition)。Bean的定義包含了BeanFactory在創(chuàng)建Bean實例時需要的所有信息。BeanFactory首先通過構(gòu)造函數(shù)創(chuàng)建一個Bean實例,之后它會執(zhí)行Bean實例的一系列之前初始化動作,初始化結(jié)束Bean將進入準備就緒(ready)狀態(tài),這時應用程序就可以獲取這些Bean實例了。最后,當你銷毀單例(SingletonBean時,它會調(diào)用相應的銷毀方法,結(jié)束Bean實例的生命周期。

    (圖-Bean的生命周期)

    Bean的定義

    前面的用戶注冊的例子中,我們已經(jīng)使用Spring定義了一個用戶持久化類:

    <bean id="userDao" class="com.dev.spring.simple.MemoryUserDao"/>

    這是一個最簡單的Bean定義。它類似于調(diào)用了語句:MemoryUserDao userDao = new MemoryUserDao()

    id屬性必須是一個有效的XML ID,這意味著它在整個XML文檔中必須唯一。它是一個Bean的“終身代號(9527)”。同時你也可以用name屬性為Bean定義一個或多個別名(用逗號或空格分開多個別名)。name屬性允許出現(xiàn)任意非法的XML字母。例如:

    <bean id="userDao" name="userDao*_1, userDao*_2"

    class="com.dev.spring.simple.MemoryUserDao"/>。

    class屬性定義了這個Bean的全限定類名(包名+類名)。Spring能管理幾乎所有的Java類。一般情況,這個Java類會有一個默認的構(gòu)造函數(shù),用set方法設置依賴的屬性。

    Bean元素出了上面的兩個屬性之外,還有很多其它屬性。說明如下:

    <bean

        id="beanId"1

        name="beanName"2

        class="beanClass"3

        parent="parentBean"4

        abstract="true | false"5

        singleton="true | false"6

        lazy-init="true | false | default"7

        autowire="no | byName | byType | constructor | autodetect | default"8

        dependency-check = "none | objects | simple | all | default"9

        depends-on="dependsOnBean"10

        init-method="method"11

        destroy-method="method"12

        factory-method="method"13

     factory-bean="bean">14

    </bean>

    1)、id: Bean的唯一標識名。它必須是合法的XML ID,在整個XML文檔中唯一。

    2)、name: 用來為id創(chuàng)建一個或多個別名。它可以是任意的字母符合。多個別名之間用逗號或空格分開。

    3)、class: 用來定義類的全限定名(包名+類名)。只有子類Bean不用定義該屬性。

    4)、parent: 子類Bean定義它所引用它的父類Bean。這時前面的class屬性失效。子類Bean會繼承父類Bean的所有屬性,子類Bean也可以覆蓋父類Bean的屬性。注意:子類Bean和父類Bean是同一個Java類。

    5)、abstract(默認為”false”):用來定義Bean是否為抽象Bean。它表示這個Bean將不會被實例化,一般用于父類Bean,因為父類Bean主要是供子類Bean繼承使用。

    6)、singleton(默認為“true”):定義Bean是否是Singleton(單例)。如果設為“true,則在BeanFactory作用范圍內(nèi),只維護此Bean的一個實例。如果設為“flase”,Bean將是Prototype(原型)狀態(tài),BeanFactory將為每次Bean請求創(chuàng)建一個新的Bean實例。

    7)、lazy-init(默認為“default”):用來定義這個Bean是否實現(xiàn)懶初始化。如果為“true”,它將在BeanFactory啟動時初始化所有的Singleton Bean。反之,如果為“false,它只在Bean請求時才開始創(chuàng)建Singleton Bean。

    8)、autowire(自動裝配,默認為“default”):它定義了Bean的自動裝載方式。

        1、“no:不使用自動裝配功能。

        2、“byName:通過Bean的屬性名實現(xiàn)自動裝配。

        3、“byType:通過Bean的類型實現(xiàn)自動裝配。

        4、“constructor:類似于byType,但它是用于構(gòu)造函數(shù)的參數(shù)的自動組裝。

        5、“autodetect:通過Bean類的反省機制(introspection)決定是使用“constructor”還是使用“byType”。

    9)、dependency-check(依賴檢查,默認為“default”):它用來確保Bean組件通過JavaBean描述的所以依賴關(guān)系都得到滿足。在與自動裝配功能一起使用時,它特別有用。

    1、none:不進行依賴檢查。

    2、objects:只做對象間依賴的檢查。

    3、simple:只做原始類型和String類型依賴的檢查

    4、all:對所有類型的依賴進行檢查。它包括了前面的objectssimple。

    10)、depends-on(依賴對象):這個Bean在初始化時依賴的對象,這個對象會在這個Bean初始化之前創(chuàng)建。

    11)、init-method:用來定義Bean的初始化方法,它會在Bean組裝之后調(diào)用。它必須是一個無參數(shù)的方法。

    12)、destroy-method:用來定義Bean的銷毀方法,它在BeanFactory關(guān)閉時調(diào)用。同樣,它也必須是一個無參數(shù)的方法。它只能應用于singleton Bean

    13)、factory-method:定義創(chuàng)建該Bean對象的工廠方法。它用于下面的“factory-bean”,表示這個Bean是通過工廠方法創(chuàng)建。此時,“class”屬性失效。

    14)、factory-bean:定義創(chuàng)建該Bean對象的工廠類。如果使用了“factory-bean”則“class”屬性失效。

    配置Bean的屬性值和Bean對象的組裝

    我們可以在Spring的配置文件中直接設置Bean的屬性值。例如:你的Bean有一個“maxSize”屬性,它表示每頁顯示數(shù)據(jù)的最大值,它有一個set方法。代碼如下:

    private int maxSize;

    public void setMaxSize(int maxSize) {

    this.maxSize = maxSize;

    }

    這樣,你可以在Bean定義時設置這個屬性的值:

    <property name="maxSize"><value>20</value></property>

           前面介紹了Bean原始類型的屬性設置。這種方式已經(jīng)可以非常有效而便利的參數(shù)化應用對象。然而,Bean工廠的真正威力在于:它可以根據(jù)bean屬性中描述的對象依賴來組裝wirebean實例。例如:userDao對象的一個屬性“sessionFactory”引用了另外一個Bean對象,即userDao對象實例依賴于sessionFactory對象:

         <bean id="userDao" class="com.dev.spring.simple.HibernateUserDao">

                  <property name="sessionFactory"><ref local="sessionFactory"/></property>

    </bean>

    <bean id="sessionFactory"

    class="org.springframework.orm.hibernate.LocalSessionFactoryBean">

           …..

           </bean>

    在這個簡單的例子中,使用<ref>元素引用了一個sessionFactory實例。在ref標簽中,我們使用了一個“local”屬性指定它所引用的Bean對象。除了local屬性之外,還有一些其它的屬性可以用來指定引用對象。下面列出<ref>元素的所有可用的指定方式:

    bean:可以在當前文件中查找依賴對象,也可以在應用上下文(ApplicationContext)中查找其它配置文件的對象。

    local:只在當前文件中查找依賴對象。這個屬性是一個XML IDREF,所以它指定的對象必須存在,否則它的驗證檢查會報錯。

    external:在其它文件中查找依賴對象,而不在當前文件中查找。

    總的來說,<ref bean="..."/><ref local="..."/>大部分的時候可以通用。“bean”是最靈活的方式,它允許你在多個文件之間共享Bean。而“local”則提供了便利的XML驗證。

    復雜的屬性值

    Springbean工廠不僅允許用String值和其他bean的引用作為bean組件的屬性值,還支持更復雜的值,例如數(shù)組、java.util.List、java.util.Mapjava.util.Properties。數(shù)組、set、listmap中的值不僅可以是String類型,也可以是其他bean的引用;map中的鍵、Properties的鍵和值都必須是String類型的;map中的值可以是set、list或者map類型

    例如:

    Null:

    <property name=“bar”><null/></property>

    List和數(shù)組:

    <property name=“bar”>

     <list>

        <value>ABC</value>

        <value>123</value>

     </list>

    </property>

    Map:

    <property name=“bar”>

     <map>

        <entry key=“key1”><value>ABC</value></entry>

        <entry key=“key2”><value>123</value></entry>

     </set>

    </property>

    Bean的之前初始化

    Bean工廠使用Bean的構(gòu)造函數(shù)創(chuàng)建Bean對象之后,緊接著它會做一件非常重要的工作——Bean的初始化。它會根據(jù)配置信息設置Bean的屬性和依賴對象,執(zhí)行相應的初始化方法。

    自動裝配

    一般不推薦在大型的應用系統(tǒng)中使用自動裝配。當然,它可以很好的用于小型應用系統(tǒng)。如果一個bean聲明被標志為“autowire(自動裝配)”,bean工廠會自動將其他的受管對象與其要求的依賴關(guān)系進行匹配,從而完成對象的裝配——當然,只有當對象關(guān)系無歧義時才能完成自動裝配。因為不需要明確指定某個協(xié)作對象,所以可以帶來很多的便利性。

    舉個例子,如果在Bean工廠中有一個SessionFactory類型的實例,HibernateUserDaosessionFactory屬性就可以獲得這個實例。這里可以使用<bean>autowire屬性,就象這樣:

    <bean id="userDao" class="com.dev.spring.simple.HibernateUserDao" autowire=”byType”>

    </bean>

    <bean id="sessionFactory"

    class="org.springframework.orm.hibernate.LocalSessionFactoryBean">

           …..

           </bean>

    注意,在userDao的定義中并沒有明確引用sessionFactory。由于它的“autowire”被設置為“byType”,所有只要userDao有一個類型為SessionFactory的屬性并有一個set方法,Bean工廠就會自動將sessionFactory組裝到userDao中。

    如果一個應用有兩個數(shù)據(jù)庫,這時就對應有兩個SessionFactory。這時autowire="byType"就無法使用了。我們可以使用另外一種自動裝配方式“byName”。它將根據(jù)屬性的名稱來匹配依賴對象,這樣如果你的配置文件中可以同時存在多個類型相同的SessionFactory,只要他們定義的名稱不同就可以了。這種方式的缺點是:需要精確匹配Bean的名稱,即必須要保證屬性的名稱和它所依賴的Bean的名稱相等,這樣比較容易出錯。

    (舉例:Aa對象依賴Bb對象。)

    注意:我們還是強烈推薦手工指定Bean之間的依賴關(guān)系。這種用法最強大,因為它允許按名稱引用特定的Bean實例,即使多個Bean具有相同類型也不會混淆。同時,你可以清楚的知道一個Bean到底依賴哪些其它的Bean。如果使用自動裝載,你只能去Bean的代碼中了解。甚至,Bean工廠也許會自動裝載一些你根本不想依賴的對象。

     


    依賴檢查

    如果你希望Bean嚴格的設置所有的屬性,“dependency-check”(依賴檢查)屬性將會非常有用。它默認為“none”,不進行依賴檢查。“simple”會核對所有的原始類型和String類型的屬性。“objects”只做對象間的關(guān)聯(lián)檢查(包括集合)。“all”會檢查所有的屬性,包括“simple”和“objects”。

    舉個例子:一個Bean有如下的一個屬性:

    private int intVar = 0;

    public void setIntVar(int intVar) {

            this.intVar = intVar;

    }

    這個Bean的配置文件設置了“dependency-check=”simple””。如果這個Bean的配置中沒有定義這個屬性“intVar”,則在進行這個Bean的依賴檢查時就會拋出異常:org.springframework.beans.factory.UnsatisfiedDependencyException

    setXXX()

    set方法非常簡單,它會給class注入所有依賴的屬性。這些屬性都必須是在配置文件中使用<property>元素定義,它們可以是原始類型,對象類型(Integer,Long),null值,集合,其它對象的引用。

    afterPropertiesSet()

    有兩種方法可以實現(xiàn)Bean的之前初始化方法。1、使用“init-method”屬性,在Spring的配置文件中定義回調(diào)方法。下面將會具體描述。2、實現(xiàn)接口InitializingBean并實現(xiàn)它的afterPropertiesSet()方法。接口InitializingBean的代碼如下:

    public interface InitializingBean {

    void afterPropertiesSet() throws Exception;

    }

    JavaBean的所有屬性設置完成以后,容器會調(diào)用afterPropertiesSet()方法,應用對象可以在這里執(zhí)行任何定制的初始化操作。這個方法允許拋出最基本的Exception異常,這樣可以簡化編程模型。

    Spring框架內(nèi)部,很多bean組件都實現(xiàn)了這些回調(diào)接口。但我們的Bean組件最好不要通過這種方式實現(xiàn)生命周期的回調(diào),因為它依賴于SpringAPI。無疑,第一種方法是我們的最佳選擇。

    init-method

    init-method的功能和InitializingBean接口一樣。它定義了一個Bean的初始化方法,在Bean的所有屬性設置完成之后自動調(diào)用。這個初始化方法不用依賴于Spring的任何API。它必須是一個無參數(shù)的方法,可以拋出Exception

    例如:我們的Bean組件UserManger中定義一個初始化方法init()。這樣,我們就可以在Bean定義時指定這個初始化方法:

    <bean id=”userManger” class=”com.dev.spring.um.DefaultUserManager”

    init-method=”init”>

    ……

    </bean>

    setBeanFactory()

    Bean的準備就緒(Ready)狀態(tài)

    Bean完成所有的之前初始化之后,就進入了準備就緒(Ready)狀態(tài)。這就意味著你的應用程序可以取得這些Bean,并根據(jù)需要使用他們。

    Bean的銷毀

    在你關(guān)閉(或重啟)應用程序時,單例(SingletonBean可以再次獲得生命周期的回調(diào),你可以在這時銷毀Bean的一些資源。第一種方法是實現(xiàn)DisposableBean接口并實現(xiàn)它的destroy()方法。更好的方法是用“destroy-method”在Bean的定義時指定銷毀方法。

    ApplicationContext

    posted on 2009-03-20 15:40 重慶理工小子 閱讀(1022) 評論(2)  編輯  收藏 所屬分類: Spring2

    FeedBack:
    # re: Sping入門(轉(zhuǎn))
    2011-02-28 22:28 | spare_H
    好軟文~~  回復  更多評論
      
    # re: Sping入門(轉(zhuǎn))
    2011-03-01 20:02 | 初學SPRING
    絕對經(jīng)典,頂  回復  更多評論
      
    主站蜘蛛池模板: 亚洲av色香蕉一区二区三区蜜桃| 亚洲一区无码中文字幕| 亚洲AV无码日韩AV无码导航| 可以免费观看一级毛片黄a| 免费看美女让人桶尿口| 中国在线观看免费国语版| 国产免费毛不卡片| 在线观看视频免费完整版| 成人免费午夜视频| 麻豆国产VA免费精品高清在线| 成年男女免费视频网站| 日韩免费一区二区三区| 国产18禁黄网站免费观看| 免费国产a国产片高清| 亚洲日韩在线观看| 亚洲精品无码精品mV在线观看| 亚洲AV中文无码乱人伦下载| 亚洲AV日韩AV永久无码免下载| 91亚洲导航深夜福利| 国产成人亚洲精品| 亚洲av永久无码一区二区三区| 亚洲成熟xxxxx电影| 亚洲小说区图片区| 亚洲精品无播放器在线播放| 午夜在线亚洲男人午在线| 四虎影视永久在线精品免费| 国产精品玖玖美女张开腿让男人桶爽免费看| 国产三级在线免费观看| 男人都懂www深夜免费网站| 免费大片黄在线观看yw| 国产女高清在线看免费观看| 亚洲色偷偷综合亚洲AV伊人| 久久91亚洲人成电影网站| 亚洲精品综合久久中文字幕| 亚洲日本va一区二区三区| 一区在线免费观看| 免费无码VA一区二区三区 | 亚洲av永久无码精品天堂久久 | 久久综合亚洲鲁鲁五月天| 亚洲综合av一区二区三区| 一级特黄特色的免费大片视频|