spring系列(一):超級經典入門

一  spring是什麼

Spring是一個開源框架,它由RodJohnson建立。它是爲了解決企業應用開發的複雜性而建立的。Spring使用基本的JavaBean來完成之前只可能由EJB完成的事情。然而,Spring的用途不只限於服務器端的開發。從簡單性、可測試性和鬆耦合的角度而言,任何Java應用均可以從Spring中受益。

目的:解決企業應用開發的複雜性

功能:使用基本的JavaBean代替EJB,並提供了更多的企業應用功能

範圍:任何Java應用

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

■  輕量——從大小與開銷兩方面而言Spring都是輕量的。完整的Spring框架能夠在一個大小隻有1MB多的JAR文件裏發佈。而且Spring所需的處理開銷也是微不足道的。此外,Spring是非侵入式的:典型地,Spring應用中的對象不依賴於Spring的特定類。

■  控制反轉——Spring經過一種稱做控制反轉(IoC)的技術促進了鬆耦合。當應用了IoC,一個對象依賴的其它對象會經過被動的方式傳遞進來,而不是這個對象本身建立或者查找依賴對象。你能夠認爲IoC與JNDI相反——不是對象從容器中查找依賴,而是容器在對象初始化時不等對象請求就主動將依賴傳遞給它。

■  面向切面——Spring提供了面向切面編程的豐富支持,容許經過分離應用的業務邏輯與系統級服務(例如審計(auditing)和事務()管理)進行內聚性的開發。應用對象只實現它們應該作的——完成業務邏輯——僅此而已。它們並不負責(甚至是意識)其它的系統級關注點,例如日誌或事務支持。

■  容器——Spring包含並管理應用對象的配置和生命週期,在這個意義上它是一種容器,你能夠配置你的每一個bean如何被建立——基於一個可配置原型(prototype),你的bean能夠建立一個單獨的實例或者每次須要時都生成一個新的實例——以及它們是如何相互關聯的。然而,Spring不該該被混同於傳統的重量級的EJB容器,它們常常是龐大與笨重的,難以使用。

■  框架——Spring能夠將簡單的組件配置、組合成爲複雜的應用。在Spring中,應用對象被聲明式地組合,典型地是在一個XML文件裏。Spring也提供了不少基礎功能(事務管理、持久化框架集成等等),將應用邏輯的開發留給了你。

全部Spring的這些特徵使你可以編寫更乾淨、更可管理、而且更易於測試的代碼。它們也爲Spring中的各類模塊提供了基礎支持。


二    spring的歷史

Spring的基礎架構起源於2000年早期,它是Rod Johnson在一些成功的商業項目中構建的基礎設施。

在2002後期,Rod Johnson發佈了《Expert One-on-One J2EE Design and Development》一書,並隨書提供了一個初步的開發框架實現——interface21開發包,interface21就是書中闡述的思想的具體實現。後來,Rod Johnson 在interface21 開發包的基礎之上,進行了進一步的改造和擴充,使其發展爲一個更加開放、清晰、全面、高效的開發框架——Spring。

2003年2月Spring框架正式成爲一個開源項目,併發佈於SourceForge中。

三  Spring的使命

¨  J2EE應該更加容易使用。

¨  面向對象的設計比任何實現技術(好比J2EE)都重要。

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

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

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

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

四   spring受到的批判

Ø  Spring不是一個「標準」。Spring不是J2EE規範的一部分,沒有經過JCP(Java Community Process)的審覈承認。

批判來源於EJB的支持者,他們認爲EJB是一個標準,是J2EE規範的一部分。固然,標準最主要的目的是但願在應用服務器之間是可移植的,但是EJB的移植卻並不輕鬆,不一樣應用服務器的ejb部署描述文件老是有着差別。並且EJB開發的類徹底依賴於EJB容器。而Spring對其管理的Bean沒有任何形式的侵入,這樣的Bean是普通Java對象(POJO),那麼它就遵循Java標準,能夠處處移植。

 

Ø  Spring是「超重量級」的。

Spring涉及的內容確實不少(例如:提供了對jdbc、ORM、遠程訪問等等的支持),但其本質仍是Java技術的龐大。Spring只是爲了這些技術提供更好的使用方案而已。同時,你能夠只選取你須要使用的部分。

五  spring包含的模塊

Spring框架由七個定義明確的模塊組成


若是做爲一個總體,這些模塊爲你提供了開發企業應用所需的一切。但你沒必要將應用徹底基於Spring框架。你能夠自由地挑選適合你的應用的模塊而忽略其他的模塊。

就像你所看到的,全部的Spring模塊都是在覈心容器之上構建的。容器定義了Bean是如何建立、配置和管理的——更多的Spring細節。當你配置你的應用時,你會潛在地使用這些類。可是做爲一名開發者,你最可能對影響容器所提供的服務的其它模塊感興趣。這些模塊將會爲你提供用於構建應用服務的框架,例如AOP和持久性。

核心容器

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

應用上下文(Context)模塊

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

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

Spring的AOP模塊

   Spring在它的AOP模塊中提供了對面向切面編程的豐富支持。這個模塊是在Spring應用中實現切面編程的基礎。爲了確保Spring與其它AOP框架的互用性, Spring的AOP支持基於AOP聯盟定義的API。AOP聯盟是一個開源項目,它的目標是經過定義一組共同的接口和組件來促進AOP的使用以及不一樣的AOP實現之間的互用性。經過訪問他們的站點http://aopalliance.sourceforge.net,你能夠找到關於AOP聯盟的更多內容。

Spring的AOP模塊也將元數據編程引入了Spring。使用Spring的元數據支持,你能夠爲你的源代碼增長註釋,指示Spring在何處以及如何應用切面函數。

JDBC抽象和DAO模塊

使用JDBC常常致使大量的重複代碼,取得鏈接、建立語句、處理結果集,而後關閉鏈接。Spring的JDBC和DAO模塊抽取了這些重複代碼,所以你能夠保持你的數據庫訪問代碼乾淨簡潔,而且能夠防止因關閉數據庫資源失敗而引發的問題。

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

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

對象/關係映射集成模塊

對那些更喜歡使用對象/關係映射工具而不是直接使用JDBC的人,Spring提供了ORM模塊。Spring並不試圖實現它本身的ORM解決方案,而是爲幾種流行的ORM框架提供了集成方案,包括Hibernate、JDO和iBATIS SQL映射。Spring的事務管理支持這些ORM框架中的每個也包括JDBC。

Spring的Web模塊

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

Spring的MVC框架

Spring爲構建Web應用提供了一個功能全面的MVC框架。雖然Spring能夠很容易地與其它MVC框架集成,例如Struts,但Spring的MVC框架使用IoC對控制邏輯和業務對象提供了徹底的分離。

它也容許你聲明性地將請求參數綁定到你的業務對象中,此外,Spring的MVC框架還能夠利用Spring的任何其它服務,例如國際化信息與驗證。


六  依賴注入(IOC)的引入

       咱們先看看需求:實現一個用戶註冊信息持久化的類。

功能:

一、 保存用戶註冊的信息;

二、 根據用戶的名稱得到該註冊用戶。

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

一、 在內存中持久化,供測試、演示使用。

二、 若是用戶的數據不多,將用戶信息持久化到文本文件中。

三、 若是用戶信息不少,並須要一些靈活的查詢,則須要使用JDBC技術將用將用戶信息持久化到數據庫中。

四、 面對企業複雜關聯的數據,甚至須要使用持久層框架來實現用戶信息的持久化,好比:iBATIS、Hibernate等。

 

如何去設計、實現咱們這個持久化類呢?

咱們遵循軟件開發的原則「首先讓它跑起來,再去優化(重構)它」,咱們首先實現最簡單的在內存中持久化用戶信息。

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

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方法
    ………..
    }

持久化類有兩個方法,分別在內存中保存和獲取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 (){
            
    }
    //將信息保存到Map中(內存存儲)
        public void saveUser(User user){
            users.put(user.getName(),user); //用用戶名作鍵值
        }
        public User LoadUser(String userName){
            return (User)users.get(userName);
        }
    }

用戶持久化類完成以後,咱們就能夠在客戶端UserRegister中使用它了。例如:用戶註冊時,UserRegister代碼片段以下:

MemoryUserPersistuserPersist = new MemoryUserPersist ();

userPersist.saveUser(user);

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

       人生的浩劫只是剛剛開始,由於根據前面的需求咱們至少要分別實現四種持久化方式!這時,你必定和我同樣在期待着救世主的早日降臨——接口(Interface)。

面向接口編程

什麼是接口?

¨  接口定義了行爲的協議,這些行爲在繼承接口的類中實現。

¨  接口定義了不少方法,可是沒有實現它們。類履行接口協議並實現全部定義在接口中的方法。

¨  接口是一種只有聲明沒有實現的特殊類。

 

接口的優勢:

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

¨  一個對象能夠很容易地被(實現了相同接口的)的另外一個對象所替換。

¨  對象間的鏈接沒必要硬綁定(hardwire)到一個具體類的對象上,所以增長了靈活性。

¨  鬆散藕合(loosens coupling)。

¨  增長了重用的可能性。

 

接口的缺點:

設計的複雜性略有增長


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

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

    public interface UserDao {
        public void save(User user);
        public User load(String name);
    }

二、 具體的持久化來必需要繼承UserDao接口,並實現它的全部方法。咱們仍是首先實現內存持久化的用戶類:

    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的實現代碼和上面的MemoryUserPersist基本相同,惟一區別是MemoryUserDao類繼承了UserDao接口,它的save()和load()方法是實現接口的方法。

這時,客戶端UserRegister的代碼又該如何實現呢?

UserDao userDao= new MemoryUserDao();

userDao.save(user);

(注:面向對象「多態」的闡述)

若是咱們再切換到文本的持久化實現TextUserDao,客戶端代碼仍然須要手工修改。雖然咱們已經使用了面向對象的多態技術,對象userDao方法的執行都是針對接口的調用,但userDao對象的建立卻依賴於具體的實現類,好比上面MemoryUserDao。這樣咱們並無徹底實現前面所說的「Client沒必要知道其使用對象的具體所屬類」。

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

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

(spring 實現用戶調用某個類的時候真正的不用再實例化某個具體的類,而是僅僅調用它的實現接口就能夠完成所要達到的目的!)


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

咱們使用一個工廠類來實現userDao對象的建立,這樣客戶端只要知道這一個工廠類就能夠了,不用依賴任何具體的UserDao實現。建立userDao對象的工廠類UserDaoFactory代碼以下:

public class UserDaoFactory {

   public static UserDao createUserDao(){

       return new MemoryUserDao();

    }

}

客戶端UserRegister代碼片段以下:

UserDao userDao= UserDaoFactory. CreateUserDao();

userDao.save(user);

如今若是再要更換持久化方式,好比使用文本文件持久化用戶信息。就算有再多的客戶代碼調用了用戶持久化對象咱們都不用擔憂了。由於客戶端和用戶持久化對象的具體實現徹底解耦。咱們惟一要修改的只是一個UserDaoFactory類。


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

到這裏人生的浩劫已經獲得了拯救。但咱們仍不知足,由於假如將內存持久化改成文本文件持久化仍然有着硬編碼的存在——UserDaoFactory類的修改。代碼的修改就意味着從新編譯、打包、部署甚至引入新的Bug。因此,咱們不知足,由於它還不夠完美!

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

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

    public class UserDaoFactory {
        public static UserDao createUserDao(){
            String className = "";
    // ……從屬性文件中取得這個UserDao的實現類全名。
            UserDao userDao = null;
            try {
                userDao = (UserDao)Class.forName(className).newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return userDao;
       }

經過對工廠模式的優化,咱們的方案已近乎完美。若是如今要更換持久化方式,不須要再作任何的手工編碼,只要修改配置文件中的userDao實現類名,將它設置爲你須要更換的持久化類名便可。

咱們終於能夠鬆下一口氣了?不,矛盾仍然存在。咱們引入了接口,引入了工廠模式,讓咱們的系統高度的靈活和可配置,同時也給開發帶來了一些複雜度:一、原本只有一個實現類,後來卻要爲這個實現類引入了一個接口。二、引入了一個接口,卻還須要額外開發一個對應的工廠類。三、工廠類過多時,管理、維護很是困難。好比:當UserDao的實現類是JdbcUserDao,它使用JDBC技術來實現用戶信息從持久化。也許要在取得JdbcUserDao實例時傳入數據庫Connection,這是仍少UserDaoFactory的硬編碼。

固然,面接口編程是實現軟件的可維護性和可重用行的重要原則已經勿庸置疑。這樣,第一個複雜度問題是沒法避免的,再說一個接口的開發和維護的工做量是微不足道的。但後面兩個複雜度的問題,咱們是徹底能夠解決的:工廠模式的終極方案——IoC模式。

重構第四步-IoC容器

使用IoC容器,用戶註冊類UserRegister不用主動建立UserDao實現類的實例。由IoC容器主動建立UserDao實現類的實例,並注入到用戶註冊類中。咱們下面將使用Spring提供的IoC容器來管理咱們的用戶註冊類。

用戶註冊類UserRegister的部分代碼以下:

    public class UserRegister {
        private UserDao userDao = null;//由容器注入的實例對象
        
        public void setUserDao(UserDao userDao){
            this.userDao = userDao;
        }
        // UserRegister的業務方法
    }

在其它的UserRegister方法中就能夠直接使用userDao對象了,它的實例由Spring容器主動爲它建立。可是,如何組裝一個UserDao的實現類到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"/>


七  總結

      Spring帶來了複雜的J2EE開發的春天。它的核心是輕量級的IoC容器,它的目標是爲J2EE應用提供了全方位的整合框架,在Spring框架下實現多個子框架的組合,這些子框架之間能夠彼此獨立,也可使用其它的框架方案加以代替,Spring但願爲企業應用提供一站式(one-stopshop)--一站式服務的解決方案。
                                        java

                                               掃碼關注我哦!spring

相關文章
相關標籤/搜索