Spring源碼學習之路---IOC(一)

一.前言

  lz最近打算再過一遍Spring,溫故而知新,想當年初入IT時不知從何入手,甚是捉急,IOC、AOP看了無數遍也不知其解,隨着工作經驗的累積,漸漸悟到了學習路線,整理出來,供大家研討。

二. 本編講點

無論你是小白還是老司機,Spring三大特性一定背吐了:
1.IOC(控制反轉) 2.DI(依賴注入) 3.AOP(面向切面);
其中,IOC和DI是緊密相連的,所以本篇文章主要講一講什麼是IOC/DI,到底有什麼用!

三.什麼是IOC,有什麼用?

  首先IOC是個容器,官方一點的話來說,最主要是完成了完成對象的創建和依賴的管理注入等等,這句話大家應該都知道,個人理解就是簡化了開發,直接上代碼感受一下,我們模擬一個購物的場景

/**
 * 〈商場〉<br>
 *
 * @author Mr.Left
 * @create 2018-12-15
 * @since 1.0.0
 */
public class Shop {
    public Person person;
    public String name;

    public Shop(Person person) {
        this.person = person;
        setName("打開淘寶");

    }

    public void open() {
        System.out.println(getName());
        person.say();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Person {
    public Pick pick;

    private String name;

    public Person(Pick pick) {
        this.pick = pick;
        setName("Jack MA");
    }

    public void say() {
        System.out.println("我是"+getName()+",我要開始買東西了!");
        pick.pick();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

/**
 * 〈挑選物品〉<br>
 *
 * @author Mr.Left
 * @create 2018-12-15
 * @since 1.0.0
 */
public class Pick {
    public Pay pay;
    private String commodity;

    public Pick(Pay pay) {
        this.pay = pay;
        setCommodity("防脫髮洗髮液");
    }

    public void pick() {
        System.out.println("我挑選了" + getCommodity());
        pay.pay();
    }

    public String getCommodity() {
        return commodity;
    }

    public void setCommodity(String commodity) {
        this.commodity = commodity;
    }
}
/**
 * 〈挑選物品〉<br>
 *
 * @author Mr.Left
 * @create 2018-12-15
 * @since 1.0.0
 */
public class Pick {
    public Pay pay;
    private String commodity;

    public Pick(Pay pay) {
        this.pay = pay;
        setCommodity("防脫髮洗髮液");
    }

    public void pick() {
        System.out.println("我挑選了" + getCommodity());
        pay.pay();
    }

    public String getCommodity() {
        return commodity;
    }

    public void setCommodity(String commodity) {
        this.commodity = commodity;
    }
}

  ok了,這一個簡單的場景就模擬出來了,爲了能更好的體會IOC的好處,Lz特意寫的麻煩些,再寫個測試接口跑一下

/**
 * 〈一句話功能簡述〉<br>
 * 〈測試接口〉
 *
 * @author Mr.Left
 * @create 2018-12-15
 * @since 1.0.0
 */
@RestController
@RequestMapping(value = "/test")
public class TestController {

    @RequestMapping(value = "/demo")
    @ResponseBody
    public String demo() {
        Pay pay = new Pay();
        Pick pick = new Pick(pay);
        Person person = new Person(pick);
        Shop shop = new Shop(person);
        shop.open();
       return "ok";
    }
}

好了,用原始的方法,我們通過對象的傳遞來維護依賴關係,那麼我們用spring的情況下,該怎麼寫呢?

/**
 * 〈商場〉<br>
 *
 * @author Mr.Left
 * @create 2018-12-15
 * @since 1.0.0
 */
@Component(value = "SpringShop")
public class Shop {

    @Autowired
    private Person person;

    public String name;

    public Shop() {
        setName("打開淘寶");

    }
    public void open() {
        System.out.println(getName());
        person.say();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

/**
 * 〈顧客〉<br>
 *
 * @author Mr.Left
 * @create 2018-12-15
 * @since 1.0.0
 */
@Component(value = "SpringPerson")
public class Person {
    
    @Autowired
    private Pick pick;

    private String name;

    public void say() {
        System.out.println("我是" + getName() + ",我要開始買東西了!");
        pick.pick();
    }

    public Person() {
        setName("Jack MA");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
/**
 * 〈挑選物品〉<br>
 *
 * @author Mr.Left
 * @create 2018-12-15
 * @since 1.0.0
 */
@Component(value = "SpringPick")
public class Pick {

    @Autowired
    private Pay pay;

    private String commodity;

    public Pick() {

        setCommodity("防脫髮洗髮液");
    }

    public void pick() {
        System.out.println("我挑選了" + getCommodity());
        pay.pay();
    }

    public String getCommodity() {
        return commodity;
    }

    public void setCommodity(String commodity) {
        this.commodity = commodity;
    }
}
/**
 * 〈付款〉<br>
 * @author Mr.Left
 * @create 2018-12-15
 * @since 1.0.0
 */
@Component(value = "SpringPay")
public class Pay {

    private String payWay;

    public  Pay(){
        setPayWay("支付寶");
    }

    public String getPayWay() {
        return payWay;
    }

    public void setPayWay(String payWay) {
        this.payWay = payWay;
    }

    public void pay() {
        System.out.println("我用" + getPayWay() + "付款");

    }
}
/**
 * 〈一句話功能簡述〉<br>
 * 〈測試接口〉
 *
 * @author Mr.Left
 * @create 2018-12-15
 * @since 1.0.0
 */
@RestController(value = "SpringTestController")
@RequestMapping(value = "/test2")
public class TestController {
    @Autowired
    private com.koolearn.donut.practise.spring.Shop shop;

    @RequestMapping(value = "/demo")
    @ResponseBody
    public String demo() {
        shop.open();
        return "ok";
    }
}

  怎麼樣,用了註解之後是不是感覺一身輕鬆,在實際項目中,一個對象不可能只依賴一個對象,大多數是依賴多個甚至十幾個,並且依賴關係是n層,維護起來讓人崩潰,有了Spring,你不用關心對象的實例化,更不用維護依賴關係,真正的實現瞭解耦!這就是程序員的春天,哈哈。
By the way,spring注入的另一種方式是xml配置,這裏就不說了

四.理解IOC思想

  lz覺得編程難點,是難在理解代碼的思想,因爲它是抽象的,和數學物理一樣,看不見摸不着,正確理解並掌握了設計思想,纔會從碼農進階爲互聯網精英,如果你是一個善於思考的人,可能會發現我們所學習到的知識似乎都遵循着某種規律,即使再抽象的東西,其本質思想,也會跟現實生活中有點聯繫,就拿IOC來說,就是把new對象的權利交給了Spring容器來處理,在現實中,不就是把一些自己嫌麻煩的事情,交給第三方來處理嘛;舉個栗子,n多年前我們打車去一個地方,只能出門攔空車,節假日和大冬天一等就是好久,有時還要拼車,你需要告訴司機你去哪,司機看順不順路,再考慮是否帶上你,這就可以看成是自己new 一個對象(出租車),乘客的目的地就像是一種依賴關係;現在好了,下一個app,輸入起點終點,交給平臺(容器)就什麼也不用管了,美滋滋。
   好了,有點扯遠了,知道了IOC的作用,我們接下來看看源碼,研究一下是怎麼實現的吧!

五.Spring IOC的結構體系

(1)BeanFactory
大名鼎鼎的Bean工廠,Spring中Bean的創建是典型的工廠模式,看一下代碼:

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";

    Object getBean(String var1) throws BeansException;

    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

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

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    <T> ObjectProvider<T> getBeanProvider(Class<T> var1);

    <T> ObjectProvider<T> getBeanProvider(ResolvableType var1);

    boolean containsBean(String var1);

    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;

    @Nullable
    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

    String[] getAliases(String var1);
}

  第一次看源碼的時候一臉矇蔽,包那麼多,那麼複雜,不過研究東西就要靜下心來,這個就是Spring核心的Bean工廠定義,好像是2001年寫的,這個類是Spring中所有的bean工廠,也就是俗稱IOC容器的祖宗(膜拜3秒鐘),各種IOC容器都只是它的實現或者爲了滿足特別需求的擴展實現,包括我們平時用的ApplicationContext。從接口中的方法名字不難看出,這些工廠的實現最大的作用就是根據bean的名稱或者類型等等,返回一個bean的實例
來看一下他的關係圖
在這裏插入圖片描述
BeanFactory作爲最頂層的一個接口類,它定義了IOC容器的基本功能規範,BeanFactory 有三個子類:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory。但是從圖中能發現最終的默認實現類是 DefaultListableBeanFactory,他實現了所有的接口。那爲何要定義這麼多層次的接口呢?查閱這些接口的源碼和說明發現,每個接口都有他使用的場合,它主要是爲了區分在 Spring 內部在操作過程中對象的傳遞和轉化過程中,對對象的數據訪問所做的限制。例如 ListableBeanFactory 接口表示這些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的是這些 Bean 是有繼承關係的,也就是每個Bean 有可能有父 Bean。AutowireCapableBeanFactory 接口定義 Bean 的自動裝配規則。這四個接口共同定義了 Bean 的集合、Bean 之間的關係、以及 Bean 行爲。
  現在我們來思考一件事情:一個工廠想要擁有這個功能,需要具備幾個因素呢?
1.需要持有各種Bean的定義,否則無法正確的完成bean的實例化;
2.需要持有bean之間的依賴關係,否則在實例化過程中也會出現問題。例如上例,我們只是各自持有person和shop,卻不知道他們的依賴關係,那麼在shop初始化後,調用open方法時,就會報空指針。這是因爲shop其實沒有真正的被正確的實例化。
3.以上兩種都要依賴我們寫依賴關係的定義,我們暫且認爲是xml文件,那麼我們需要一個工具來讀取它
在我的理解中,只要滿足上述三種條件,便可以創建一個bean工廠,當然,Spring還有更高級的做法,以上只是我直觀的去想如何實現IOC。
  接下來接着思考,第一步,如何持有Bean的定義?我們知道在Spring的Xml配置文件中,有個lazy-init的屬性,這就說明我們是可以控制bean在何時實例化的。這個屬性默認是false的,也就是說Spring容器初始化後,配置了延遲加載的bean都還未產生,只是存儲了bean的定義,而非實例,在需要的時候,它纔會出現,接下來,再介紹一個祖宗級別的接口:BeanDefinition

package org.springframework.beans.factory.config;

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

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
    String SCOPE_SINGLETON = "singleton";
    String SCOPE_PROTOTYPE = "prototype";
    int ROLE_APPLICATION = 0;
    int ROLE_SUPPORT = 1;
    int ROLE_INFRASTRUCTURE = 2;

    void setParentName(@Nullable String var1);

    @Nullable
    String getParentName();

    void setBeanClassName(@Nullable String var1);

    @Nullable
    String getBeanClassName();

    void setScope(@Nullable String var1);

    @Nullable
    String getScope();

    void setLazyInit(boolean var1);

    boolean isLazyInit();

    void setDependsOn(@Nullable String... var1);

    @Nullable
    String[] getDependsOn();

    void setAutowireCandidate(boolean var1);

    boolean isAutowireCandidate();

    void setPrimary(boolean var1);

    boolean isPrimary();

    void setFactoryBeanName(@Nullable String var1);

    @Nullable
    String getFactoryBeanName();

    void setFactoryMethodName(@Nullable String var1);

    @Nullable
    String getFactoryMethodName();

    ConstructorArgumentValues getConstructorArgumentValues();

    default boolean hasConstructorArgumentValues() {
        return !this.getConstructorArgumentValues().isEmpty();
    }

    MutablePropertyValues getPropertyValues();

    default boolean hasPropertyValues() {
        return !this.getPropertyValues().isEmpty();
    }

    void setInitMethodName(@Nullable String var1);

    @Nullable
    String getInitMethodName();

    void setDestroyMethodName(@Nullable String var1);

    @Nullable
    String getDestroyMethodName();

    void setRole(int var1);

    int getRole();

    void setDescription(@Nullable String var1);

    @Nullable
    String getDescription();

    boolean isSingleton();

    boolean isPrototype();

    boolean isAbstract();

    @Nullable
    String getResourceDescription();

    @Nullable
    BeanDefinition getOriginatingBeanDefinition();
}

SpringIOC容器管理了我們定義的各種Bean對象及其相互的關係,Bean對象在Spring實現中是以BeanDefinition來描述的,其繼承體系如下:
在這裏插入圖片描述
  Bean 的解析過程非常複雜,功能被分的很細,因爲這裏需要被擴展的地方很多,必須保證有足夠的靈活性,以應對可能的變化。Bean 的解析主要就是對 Spring 配置文件的解析。這個解析過程主要通過下圖中的類完成:
在這裏插入圖片描述
  再看看源碼,這個便是spring中的bean定義接口,所以其實我們工廠裏持有的bean定義,就是一堆這個玩意,或者是他的實現類和子接口。這個接口並非直接的祖宗接口,他所繼承的兩個接口一個是core下面的AttributeAccessor,繼承這個接口就以爲這我們的bean定義接口同樣具有處理屬性的能力,而另外一個是beans下面的BeanMetadataElement,字面翻譯這個接口就是bean的元數據元素,它可以獲得bean的配置定義的一個元素。在XML文件中來說,就是會持有一個bean標籤。
  仔細觀看,能發現beanDefinition中有兩個方法分別是String[] getDependsOn()和void setDependsOn(String[] dependsOn),這兩個方法就是獲取依賴的beanName和設置依賴的beanName,這樣就好辦了,只要我們有一個BeanDefinition,就可以完全的產生一個完整的bean實例。
  今天就先到這裏吧,lz菜的摳腳,也是邊看邊寫的,費了很大勁才梳理出一點東西,更多的是看到哪寫到哪,IOC的實現原理用到了反射,我接下來想想怎麼寫進去。
  文中若有欠妥的地方,希望大家指出。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章