Spring框架底層原理-IoC

1、概述

  • Spring是一個輕量級的開源JavaEE框架
  • Spring能夠解決企業應用開發的複雜性
  • Spring兩大核心部分:IoC和AOP
  • 特色:
    • 方便解耦,簡化開發
    • AOP編程支持
    • 方便程序測試
    • 方便和其餘框架整合
    • 方便事務操做
    • 下降API開發難度

IoC和AOP是Spring的核心,我們就從這兩個倆分析其原理,入門案例這裏就不寫了,直接進入主題,先來說述IoC,下篇文章將講述AOPjava

2、IoC解耦推導

咱們都知道,IoC是控制反轉,通俗講就是把對象建立和對象之間的調用交給Spring管理,經過簡單的xml配置就能夠建立和調用對象,其主要目的就是解耦,下降代碼之間的耦合度,我們就從傳統方式到IoC來一步一步講述怎樣把耦合度降到最低。編程

所謂解耦就是下降類之間的依賴和方法之間的依賴設計模式

1. 傳統直接調用對象

在咱們傳統的開發方式中,是直接採起new對象的方式建立對象,常常能夠看到service調用dao這樣的代碼,若是是直接調用,咱們來看看是怎麼樣子的,建立UserService和UserDao ,經過UserService調用UserDao:markdown

// UserDao1類
public class UserDao1 {
    public void say(){
        System.out.println("I am userDao1");
    }
}

// UserService類,調用UserDao1類中的方法
public class UserService {
    public void getUser(UserDao1 userDao1){
        userDao1.say();
    }
}
複製代碼

在上面的代碼中,是最傳統的調用方式,經過service調用dao,能夠獲得咱們想要的結果,打印出:「I am userDao1」,可是,忽然,產品經理想要UserDao2一個新的類也能夠在UserService中進行調用,這個時候,就須要將代碼改成以下:框架

// UserDao1類
public class UserDao1 {
    public void say(){
        System.out.println("I am userDao1");
    }
}

// UserDao2類
public class UserDao2 {
    public void say(){
        System.out.println("I am userDao2");
    }
}


// UserService類,調用UserDao1和UserDao2類中的方法
public class UserService {
    public void getUser(UserDao1 userDao1){
        userDao1.say();
    }
    
    public void getUser(UserDao2 userDao2){
        userDao2.say();
    }
}
複製代碼

能夠看到,咱們不只要新建一個UserDao2類,還須要修改UserService中的代碼,萬一,忽然,產品經理想把UserDao三、UserDao四、UserDao5.....這樣具備相同功能的類也在userService中做爲參數進行調用,新建這些類倒還好,避免不了,問題是還要修改UserService類,簡直頭大....ide

其實,上面的代碼中,UserService就和要調用的dao類具備一種很強的聯繫,咱們把這種聯繫稱爲強耦合關係,這種強耦合關係是不利於開發的,所以咱們須要解耦,首先想到的即是使用接口進行解耦,也就是面向接口編程。oop

2. 接口解耦

將上面的代碼進行修改,將UserDao定義爲接口,而後去實現這個接口,再進行調用,以下:測試

// UserDao接口
public interface UserDao {
    void say();
}

// 接口實現類UserDao1
public class UserDaoImpl1 implements UserDao {
    @Override
    public void say() {
        System.out.println("I am userDao1");
    }
}

// 接口實現類UserDao2
public class UserDaoImpl2 implements UserDao {
    @Override
    public void say() {
        System.out.println("I am userDao2");
    }
}


// UserService中進行調用
public class UserService {
    public void getUser(UserDao userDao){
        userDao.say();
    }
}
複製代碼

在上面的代碼中,咱們能夠看到,UserService類中getUser方法參數能夠是UserDao1類型的,也能夠是UserDao2類型的,不像以前的代碼,只能是指定的UserDao。這時,UserService和UserDao一、UserDao2聯繫的就沒那麼緊密了,這是一種弱耦合關係,經過接口來進行解耦。spa

可是仔細查看上面的代碼,你會發現,接口和實現類之間仍是存在強耦合關係,在面向接口編程中,咱們經常會看到相似這樣的代碼:.net

UserDao userDao = new UserDaoImpl1();
複製代碼

假設如今不用這個UserDaoImpl1了,而改用UserDao的另外一個實現類UserDaoImpl2,代碼就要改成以下:

UserDao userDao = new UserDaoImpl2();
複製代碼

這樣也就是接口和實現類出現了耦合,爲了進一步解耦,咱們就使用下面的工廠模式。

3. 工廠模式解耦

工廠的意思也就是一個批量製造一樣規格(規格也就是接口類所提供好的規範)類的類,所謂的工廠模式也就是將全部的建立對象任務交給了一個「中間人」,也就是工廠類來實現,要想使用對象,直接找工廠類,實現類必需要從工廠中取出來。對工廠模式有疑問的能夠參考我以前的文章:Java 中設計模式 之 工廠模式

而要使用工廠模式進行解耦,咱們須要先將建立對象交給工廠類:

// 工廠類
public class BeanFactory {
    // 建立並返回UserDaoImpl1
    public static UserDao getUserDao1(){
        return new UserDaoImpl1();
    }

    // 建立並返回UserDaoImpl2
    public static UserDao getUserDao2(){
        return new UserDaoImpl2();
    }
}
複製代碼

將建立對象交給工廠類,調用關係就轉變爲以下:

UserDao userDao = new UserDaoImpl1();  ===>  UserDao userDao1 = BeanFactory.getUserDao1();
UserDao userDao = new UserDaoImpl2();  ===>  UserDao userDao2 = BeanFactory.getUserDao2();
複製代碼

這樣一來,咱們建立對象只須要調用工廠類BeanFactory中的方法便可,調用時不是直接經過接口,而是經過工廠類,將建立對象交給了工廠類,就下降了接口和實現類之間的耦合。

上面的方法雖然下降了接口和實現類之間的耦合度,可是,這樣接口和工廠類之間就產生了耦合,爲了再次解耦,咱們引入了反射+xml配置文件的方式進行再次解耦。

4. xml 配置 + 反射 + 工廠解耦(IoC底層的實現)

使用xml配置文件

<bean id="userDao" class="**.UserDaoImpl">
複製代碼

工廠類

class BeanFactory {
    public static UserDao getUserDao(String id) {
        // String className = 解析配置文件xml 拿到id對應的class
        // 反射
        class clazz = class.forName(className);
        return clazz.newInstance();
    }
}
複製代碼

能夠看到,在這個工廠類中,並無直接像上面那樣直接new對象,而是使用了xml解析和反射方式建立對象,分析以下:

  • 經過xml解析獲取對象中屬性的值
  • 經過反射獲得字節碼文件
  • 經過字節碼文件建立對象

這樣的話若是咱們須要改UserDao的實現類的類型,咱們能夠直接在配置文件中修改,就不須要修改代碼,這就是IoC的解耦。

3、IoC 原理理解

如下部分是開濤這位技術牛人對Spring框架的IOC的理解,寫得很是通俗易懂,原文地址:jinnianshilongnian.iteye.com/blog/141384…

1. IoC是什麼

IoC:Inversion of Control(控制反轉),這不是什麼技術,而是一種設計思想,在java開發中,IoC意味着將你設計好的對象交給容器,而不是傳統的在你的對象內部直接控制,如何理解好Ioc呢?理解好IoC的關鍵是要明確「誰控制誰,控制什麼,爲什麼是反轉(有反轉就應該有正轉了),哪些方面反轉了」,那咱們來深刻分析一下:

  • 誰控制誰,控制什麼:傳統Java SE程序設計,咱們直接在對象內部經過new進行建立對象,是程序主動去建立依賴對象;而IoC是有專門一個容器來建立這些對象,即由IoC容器來控制對 象的建立;誰控制誰?固然是IoC 容器控制了對象;控制什麼?那就是主要控制了外部資源獲取(不僅是對象包括好比文件等)
  • 爲什麼是反轉,哪些方面反轉了:有反轉就有正轉,傳統應用程序是由咱們本身在對象中主動控制去直接獲取依賴對象,也就是正轉;而反轉則是由容器來幫忙建立及注入依賴對象;爲什麼是反轉?由於由容器幫咱們查找及注入依賴對象,對象只是被動的接受依賴對象,因此是反轉;哪些方面反轉了?依賴對象的獲取被反轉了。

2. IoC能作什麼

IoC 不是一種技術,只是一種思想,一個重要的面向對象編程的法則,它能指導咱們如何設計出鬆耦合、更優良的程序。傳統應用程序都是由咱們在類內部主動建立依賴對象,從而致使類與類之間高耦合,難於測試;有了IoC容器後,把建立和查找依賴對象的控制權交給了容器,由容器進行注入組合對象,因此對象與對象之間是 鬆散耦合,這樣也方便測試,利於功能複用,更重要的是使得程序的整個體系結構變得很是靈活。

其實IoC對編程帶來的最大改變不是從代碼上,而是從思想上,發生了「主從換位」的變化。應用程序本來是老大,要獲取什麼資源都是主動出擊,可是在IoC/DI思想中,應用程序就變成被動的了,被動的等待IoC容器來建立並注入它所須要的資源了。

  IoC很好的體現了面向對象設計法則之一—— 好萊塢法則:「別找咱們,咱們找你」;即由IoC容器幫對象找相應的依賴對象並注入,而不是由對象主動去找。

3. IoC和DI

DI:DI—Dependency Injection,即「依賴注入」組件之間依賴關係由容器在運行期決定,形象的說,即由容器動態的將某個依賴關係注入到組件之中。**依賴注入的目的並不是爲軟件系統帶來更多功能,而是爲了提高組件重用的頻率,併爲系統搭建一個靈活、可擴展的平臺。**經過依賴注入機制,咱們只須要經過簡單的配置,而無需任何代碼就可指定目標須要的資源,完成自身的業務邏輯,而不須要關心具體的資源來自何處,由誰實現。

  理解DI的關鍵是:「誰依賴誰,爲何須要依賴,誰注入誰,注入了什麼」,那咱們來深刻分析一下:

  • 誰依賴於誰:固然是應用程序依賴於IoC容器

  • 爲何須要依賴:應用程序須要IoC容器來提供對象須要的外部資源

  • 誰注入誰:很明顯是IoC容器注入應用程序某個對象,應用程序依賴的對象

  • 注入了什麼:就是注入某個對象所須要的外部資源(包括對象、資源、常量數據)

  IoC和DI由什麼關係呢?其實它們是同一個概念的不一樣角度描述,因爲控制反轉概念比較含糊(可能只是理解爲容器控制對象這一個層面,很難讓人想到誰來維護對象關係),因此2004年大師級人物Martin Fowler又給出了一個新的名字:「依賴注入」,相對IoC 而言,「依賴注入」明確描述了「被注入對象依賴IoC容器配置依賴對象」。

爲了更好的理解,我找了一個例子:

一我的(Java實例,調用者)須要一把斧子(Java實例,被調用者)

在原始社會裏,幾乎沒有社會分工;須要斧子的人(調用者)只能本身去磨一把斧子(被調用者);對應情形爲:Java程序裏的調用者本身建立被調用者,一般採用new關鍵字調用構造器建立一個被調用者

進入工業社會,工廠出現了,斧子再也不由普通人完成,而在工廠裏被生產出來,此時須要斧子的人(調用者)找到工廠,購買斧子,無須關心斧子的製造過程;對應簡單工廠設計模式,調用者只需定位工廠,無須管理被調用者的具體實現

進入「共產主義」社會,須要斧子的人甚至無須定位工廠,「坐等」社會提供便可;調用者無須關心被調用者的實現,無須理會工廠,等待Spring依賴注入

總之依賴注入的意思是你須要的東西不是由你建立的,而是第三方,或者說容器提供給你的。這樣的設計符合正交性,即所謂的鬆耦合。

IoC的底層原理就到這裏,下一篇將講述AOP的底層原理

相關文章
相關標籤/搜索