spring源碼學習之路---IOC初探(二)


          作者:zuoxiaolong8810(左瀟龍),轉載請注明出處,特別說明:本博文來自博主原博客,為保證新博客中博文的完整性,特復制到此留存,如需轉載請注明新博客地址即可。

          上一章當中我沒有提及具體的搭建環境的步驟,一個是不得不承認有點懶,另外一個我覺得如果上章所述的那些環境都還不會搭建的話,研究spring的源碼還有些過早。

          如果你有興趣的話,相信已經搭建好了學習研究的環境,接下來就可以進入正題了。

          網上也有很多關於spring源碼學習的文章以及帖子,講的也都不錯,但是有些可能高估了讀者的能力,該深入的地方反倒一句帶過,我現在也是在一步一步研究,和大家的進度一樣,所以可能在我的角度來和各位探討,更加容易。

          首先我們來說一下IOC,IOC是spring最核心的理念,包括AOP也要屈居第二,那么IOC到底是什么呢,四個字,控制反轉。

          網上有不少是這么解釋IOC的,說IOC是將對象的創建和依賴關系交給容器,這句話我相信不少人都知道,在我個人的理解,IOC就是讓我們的開發變的更簡單了。

          為什么這么說呢,光說沒意思,直接上代碼。

 public class Person {

    public void work(){
        System.out.println("I am working");
    }
}

         上面這個是Person類,如果我們還有一個Company公司類,公司要開張需要人來工作,所以我們可能需要這樣。

public class Company {

    public Person person;
    
    public Company(Person person){
        this.person = person;
    }
    
    public void open(){
        person.work();
        System.out.println("I am opening");
    }
}

         好了,這樣可以了,雖說和現實有些區別,畢竟沒有一個人的公司,但是就是這么個意思。必須要有人在公司里,公司才能開張。

         有了spring上述情況我們是怎么寫的呢?Person類不變,Company就可以簡單些了。

public class Company {

    @Autowired
    public Person person;
    
    public void open(){
        person.work();
        System.out.println("I am opening");
    }
}

         OK了,使用注解后,spring里的寫法是這樣的,是不是簡單很多?或許你可能會說,這才減少了多少代碼,但是事實上是,真正的項目中,不可能有這么簡單的依賴關系,或許是2層,3層甚至N層。

         當然,可能我們有時候用的XML,XML和注解的區別就在於這里,注解可以快速的完成依賴的注入,但是缺點也很明顯,那就是比如我公司里不需要人了,我需要的是機器,那么我還要手動改代碼,將Person換成機器(這里應該是英文,英語不好,懶得查了,只記得念“磨洗”),而如果是XML配置,那么我們只需要改下配置文件就可以。維護起來會方便很多,當然XML的缺點也很明顯,那就是依賴關系復雜的時候,XML文件會比較臃腫,所以我們一般的做法是將XML分離開來。

         說到這里,有些扯遠了,但是我覺得以上可以足夠說明IOC的好處,知道了IOC的好處,我們自然就要知道怎么來實現IOC了。

         或許看了spring的源碼,第一感覺是很蒙,包太多,我也很蒙,但是研究東西就是得沉下心來,先來關注一下這個接口,BeanFactory,附上代碼。

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;

/*
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @since 13 April 2001
 */
public interface BeanFactory {

    String FACTORY_BEAN_PREFIX = "&";


    Object getBean(String name) throws BeansException;


    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    
    <T> T getBean(Class<T> requiredType) throws BeansException;

    
    Object getBean(String name, Object... args) throws BeansException;

    
    boolean containsBean(String name);

    
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    
    boolean isTypeMatch(String name, Class targetType) throws NoSuchBeanDefinitionException;

    
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    
    String[] getAliases(String name);

}

          這個便是spring核心的Bean工廠定義,上面的author說是2001年寫的,已經歷史久遠了,這個類是spring中所有bean工廠,也就是俗稱的IOC容器的祖宗,各種IOC容器都只是它的實現或者為了滿足特別需求的擴展實現,包括我們平時用的最多的ApplicationContext。從上面的方法就可以看出,這些工廠的實現最大的作用就是根據bean的名稱亦或類型等等,來返回一個bean的實例。

          一個工廠如果想擁有這樣的功能,那么它一定需要以下幾個因素:

          1.需要持有各種bean的定義,否則無法正確的完成bean的實例化。

          2.需要持有bean之間的依賴關系,否則在bean實例化的過程中也會出現問題。例如上例,如果我們只是各自持有Person和Company,卻不知道他們的依賴關系,那么在Company初始化以后,調用open方法時,就會報空指針。這是因為Company其實並沒有真正的被正確初始化。

          3.以上兩種都要依賴於我們所寫的依賴關系的定義,暫且認為是XML文件(其實可以是各種各樣的),那么我們需要一個工具來完成XML文件的讀取。

          我目前想到的,只需要滿足以上三種條件,便可以創建一個bean工廠,來生產各種bean。當然,spring有更高級的做法,以上只是我們直觀的去想如何實現IOC。

          其實在上述過程中仍舊有一些問題,比如第一步,我們需要持有bean的定義,如何持有?這是一個問題。我們知道spring的XML配置文件中,有一個屬性是lazy-init,這就說明,bean在何時實例化我們是可以控制的。這個屬性默認是false,但是我們可以將這個屬性設置為true,也就是說spring容器初始化以后,配置了延遲加載的各種bean都還未產生,它們只在需要的時候出現。

          所以我們無法直接的創建一個Map<String,Object>來持有這些bean的實例,在這里要注意,我們要儲存的是bean的定義,而非實例。

          那么接下來,又是一個祖宗級別的接口要出現了,來看BeanDefinition。

package org.springframework.beans.factory.config;

import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.core.AttributeAccessor;


public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {


    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;


    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;


    int ROLE_APPLICATION = 0;


    int ROLE_SUPPORT = 1;


    int ROLE_INFRASTRUCTURE = 2;


    String getParentName();


    void setParentName(String parentName);


    String getBeanClassName();


    void setBeanClassName(String beanClassName);


    String getFactoryBeanName();

    
    void setFactoryBeanName(String factoryBeanName);

    
    String getFactoryMethodName();

    
    void setFactoryMethodName(String factoryMethodName);


    String getScope();

    
    void setScope(String scope);

    
    boolean isLazyInit();

    
    void setLazyInit(boolean lazyInit);

    
    String[] getDependsOn();

    
    void setDependsOn(String[] dependsOn);

    
    boolean isAutowireCandidate();

    
    void setAutowireCandidate(boolean autowireCandidate);

    
    boolean isPrimary();

    
    void setPrimary(boolean primary);


    ConstructorArgumentValues getConstructorArgumentValues();


    MutablePropertyValues getPropertyValues();


    boolean isSingleton();


    boolean isPrototype();

    
    boolean isAbstract();

    
    int getRole();

    
    String getDescription();

    
    String getResourceDescription();

    
    BeanDefinition getOriginatingBeanDefinition();

}

            顧名思義,這個便是spring中的bean定義接口,所以其實我們工廠里持有的bean定義,就是一堆這個玩意,或者是他的實現類和子接口。這個接口並非直接的祖宗接口,他所繼承的兩個接口一個是core下面的AttributeAccessor,繼承這個接口就以為這我們的bean定義接口同樣具有處理屬性的能力,而另外一個是beans下面的BeanMetadataElement,字面翻譯這個接口就是bean的元數據元素,它可以獲得bean的配置定義的一個元素。在XML文件中來說,就是會持有一個bean標簽。

            仔細觀看,能發現beanDefinition中有兩個方法分別是String[] getDependsOn()和void setDependsOn(String[] dependsOn),這兩個方法就是獲取依賴的beanName和設置依賴的beanName,這樣就好辦了,只要我們有一個BeanDefinition,就可以完全的產生一個完整的bean實例。

            今天就先到這里吧,我也是邊看邊寫的,與其說跟我一起學,其實這個系列的應該叫源碼學習筆記,更多的還是看到哪里,寫到哪里,談不上跟我一起學。

            如果文中有不周到的地方,希望各位盡管指出。

           

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM