Spring_IOC/DI

1、問題引入

在說Spring之前,首先來回顧一下我們之前學習的javaSE部分的知識,假如我們現在有一個需求:現在有兩個類(A類和B類),我們需要在第二個類(B)中調用第一個類(A)的某些方法,用我們以前學過的知識,肯定是在第二個類中實例化一個A類的對象,然後用對象調用對應的方法,具體操作如下:

但是這麼做並不是一個好的方法,因爲這樣做我們可以理解爲是A類 ‘污染’ 了B類,或者說是A類 ‘ 侵入 ’ 了B類。我們可以這麼理解,假如現在一個項目中有很多個類都用這種方式調用了A類的方法,但是隨着技術的更新有一天我們發現A類現在已經毫無存在的價值了,我們有了更好的方式去代替它,但是如果這個時候要刪掉A類,那麼每一個調用了A類這個方法的類都需要手動去修改源代碼。這樣就會給我們帶來很大的麻煩。因此,我們應該用另一種更好的方法區代替上面的操作。

這個時候我們可能會想到使用接口,讓A、B兩個類實現同樣的一個接口,然後覆寫接口裏面的方法,這樣看起來似乎也沒有多大的問題:

這樣看起來是和A類沒有多大的關係,但是使用這種還是擺脫不了new一個對象的命運。和這個比起來,我們更加希望:假如現在有這樣一個 ‘接口’ ,當我們需要哪一個類的對象的時候,由這個特殊的接口直接給我們返回這個對象的實例,而不用讓我們自己手動去new一個對象,增加代碼的耦合度。比如:

其實上面這種思想就是Spring中的IOC/DI思想。

2、IOC 和 DI

2.1 什麼是IOC和DI

IOC的全稱爲 Inversion of Control ,顧名思義就是控制反轉

在傳統方法中,當一個類中需要另一個類的實例化對象,通常都是自己手動去new一個,而有了IOC之後,IOC中有一個容器,這個容器就相當於上面提到的“特殊的接口”(注意:容器不等於接口,我這裏說它是一個特殊的接口,只是舉個例子方便描述它的功能而已)。這個容器負責創建實例化對象,然後再在適當的時候主動將對象給需要依賴這個對象的類。

所謂的控制反轉:

  • 控制的就是上面說的創建對象的過程(也就是new的過程);誰控制誰 —— IOC容器控制對象的創建
  • 反轉:有反轉就有正轉,像我們的傳統方法,自己需要什麼就自己動手去創建什麼,這個就是正轉;而通過容器控制對象的創建以及依賴對象的注入,這個過程就是反轉。因爲是容器幫我們查找並注入依賴對象,所以對象只是被動的接收依賴對象,所以叫做反轉。反轉的就是依賴對象獲取的過程。

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

依賴注入和控制反轉之間的關係:

依賴注入和控制反轉其實描述的是同一個東西,只是從不同的角度來描述而已。控制反轉就是爲一個對象動態的提供它所需要的另一個對象,而這個過程是通過依賴注入來實現的。比如:B類需要A類的一個對象,然後容器new一個A的對象,在適當的時候,就像打針一樣,把A的對象注射到B裏面,這樣就實現了依賴注入。

IOC不是什麼新技術,而是一種思想。下面在通過一個簡單的例子去理解IOC和傳統方法的區別;

就拿找女朋友來說,剛開始,我們是在茫茫人海中自己去尋找,我們自己知道我們需要的是怎麼樣一個人,當我們發現一個目標之後,開始自己私下去打聽她的qq,微型,手機號......製造巧合去認識她,然後投其所好......這一系列的過程都需要我們自己去操作,然而,現在有了IOC,就相當於是一個婚介所,他們那裏存了整個村子的男男女女的資料,這個時候我們只需要將我們的要求告訴他們,然後讓他們找到合適的對象之後再主動給我,如果她給我的這個對象合適了,我們就談談戀愛、然後結婚......如果不合適,就拋出異常。這整個過程不用我們自己去操作,而是交給了婚介所這樣的一個容器去控制整個過程。Spring也就是這個思想:所有的類都會在spring容器中登記,告訴spring你是個什麼東西,你需要什麼東西,然後spring會在系統運行到適當的時候,把你要的東西主動給你,同時也把你交給其他需要你的東西。所有的類的創建、銷燬都由 spring來控制,也就是說控制對象生存週期的不再是引用它的對象,而是spring。對於某個具體的對象而言,以前是它控制其他對象,現在是所有對象都被spring控制,所以這叫控制反轉。

3、在代碼中簡單體驗IOC和DI

繼續使用上面的A,B兩個類:

A類:

在配置文件中配置A類

在B類中獲取A類的實例對象

運行結果:

如果是新手,看來這個可能還是一臉茫然,不知道是怎麼回事,下面會有完整的Spring項目創建以及IOC和DI的實例;

4、Spring屬性實例化案例

使用工具:IDEA+maven

首先,創建一個maven項目(前提是你電腦中已經安裝好了maven)

早maven項目的pom文件中導入依賴的jar包:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
</dependencies>

在resource文件下創建一個配置文件(.xml) (如 applicationContext.xml)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

<!--在這裏配置類、屬性信息-->

</beans>

這是下面例子的目錄結構:

幾個類結構:


import java.util.*;

/**
 * 測試各種數據類型的屬性注入
 */
public class MyTest {

    private Integer intVal ; // 整形數據
    private String strVal ; // String 數據
    private String[] strArr ; // 數組類型
    private List list ; // List集合
    private Set set ;  // Set集合
    private Map map; // Map集合
    private User user; //自定義類型
    private Date date; // 日期類型

    // 爲每一個屬性添加一個get和set方法 通過set方法實現屬性注入,所以必須要有set方法
    public void setIntVal(Integer intVal) {
        this.intVal = intVal;
    }

    public void setStrVal(String strVal) {
        this.strVal = strVal;
    }

    public void setStrArr(String[] strArr) {
        this.strArr = strArr;
    }

    public void setList(List list) {
        this.list = list;
    }

    public void setSet(Set set) {
        this.set = set;
    }

    public void setMap(Map map) {
        this.map = map;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public void setDate(Date date) {
        this.date = date;
    }

//    覆寫toString方法用於打印屬性信息
    @Override
    public String toString() {
        return " intVal = " + intVal +
                "\r\n strVal = '" + strVal + '\'' +
                "\r\n strArr = " + Arrays.toString(strArr) +
                "\r\n list = " + list +
                "\r\n set = " + set +
                "\r\n map = " + map +
                "\r\n date = " + date +
                "\r\n user = " + user ;
    }
}
/**
 * 用於測試自定義屬性注入的User類
 */
public class User {
    private Integer age ;
    private String name ;

    public void setAge(Integer age) {
        this.age = age;
    }

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

    // 覆寫toString方法用於打印信息
    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

4.1 通過set方法實例化:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

<!--在這裏配置類、屬性信息-->
    <bean id="test" class="per.fei._3SpringProjectTest.MyTest">

        <!--整形的注入-->
        <property name="intVal">
            <value>18</value>
        </property>
        <!--整形也可以這麼弄-->
        <!--這裏寫的是String ("18"),而類中的類型是Integer ( = 18),spring內部實現了從String到Integer的轉換-->
        <!--<property name="intVal" value="18"/>-->

        <!--String類型-->
        <property name="strVal" value="hahaha"/>

        <!--數組類型-->
        <property name="strArr">
           <list>
               <value>strArr_1</value>
               <value>strArr_2</value>
               <value>strArr_3</value>
           </list>
        </property>

        <!--list就是一個動態數組,所以和數組基本一樣-->
        <property name="list">
            <list>
                <value>list_1</value>
                <value>list_2</value>
                <value>list_3</value>
            </list>
        </property>

        <!--set類型的數據-->
        <property name="set">
            <set>
                <value>set_01</value>
                <value>set_02</value>
                <value>set_03</value>
            </set>
        </property>

        <!--map類型-->
        <property name="map" >
            <map>
                <entry key="1" value="no_1"/>
                <entry key="2" value="no_2"/>
                <entry key="3" value="no_3"/>
            </map>
        </property>

        <!--Date類型不能直接像下面這麼弄,要先寫一個類型轉換器(MyConvert.java),
因爲Spring雖然在上面說實現了從String到Integer的類型轉換,-->
        <!--但是沒有實現從String到Date的類型轉換,需要我們自己實現一個轉換器-->
        <property name="date" value="2019-5-21"/>

        <!--自定義類型-可以先配置對應的自定義類型的bean,然後通過ref(反射)直接獲取對應的自定義類型-->
        <property name="user" ref="user"/>


    </bean>

    <!--配置User類-->
    <bean id="user" class="per.fei._3SpringProjectTest.User">
        <property name="name" value="張三"/>
        <property name="age" value="22"/>
    </bean>

    <!--自定義屬性編輯器到Spring中-->
    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map >
                <entry key="java.util.Date" value="per.fei._3SpringProjectTest.MyConvert"/>
            </map>
        </property>
    </bean>

</beans>

測試結果:

4.2 通過構造方法實例化:

(這裏需要把上面的MyTest類中的set方法換成構造方法,其餘類結構的不變)

MyTest.java

import java.util.*;
/**
 * 測試各種數據類型的屬性注入
 */
public class MyTest {

    private Integer intVal ; // 整形數據
    private String strVal ; // String 數據
    private String[] strArr ; // 數組類型
    private List list ; // List集合
    private Set set ;  // Set集合
    private Map map; // Map集合
    private User user; //自定義類型
    private Date date; // 日期類型

//   添加構造方法
    public MyTest(Integer intVal, String strVal, String[] strArr, List list, Set set, Map map, User user, Date date) {
        this.intVal = intVal;
        this.strVal = strVal;
        this.strArr = strArr;
        this.list = list;
        this.set = set;
        this.map = map;
        this.user = user;
        this.date = date;
    }

    //    覆寫toString方法用於打印屬性信息
    @Override
    public String toString() {
        return " intVal = " + intVal +
                "\r\n strVal = '" + strVal + '\'' +
                "\r\n strArr = " + Arrays.toString(strArr) +
                "\r\n list = " + list +
                "\r\n set = " + set +
                "\r\n map = " + map +
                "\r\n date = " + date +
                "\r\n user = " + user ;
    }
}

配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

<!--在這裏配置類、屬性信息-->
    <bean id="test" class="per.fei._3SpringProjectTest.MyTest">
        <!--int型-->
        <constructor-arg name="intVal" value="18"/>
        <!--String類型-->
        <constructor-arg name="strVal" value="hahaha"/>
        <!--數組類型-->
        <constructor-arg name="strArr">
            <list>
                <value>strArr_01</value>
                <value>strArr_02</value>
                <value>strArr_03</value>
            </list>
        </constructor-arg>
        <!--list類-->
        <constructor-arg name="list">
            <list>
                <value>list_01</value>
                <value>list_02</value>
                <value>list_03</value>
            </list>
        </constructor-arg>
        <!--set類型-->
        <constructor-arg name="set">
            <set>
                <value>set_01</value>
                <value>set_02</value>
                <value>set_03</value>
            </set>
        </constructor-arg>
        <!--map類型-->
        <constructor-arg name="map">
            <map>
                <entry key="mapKey_01" value="mapVal_01"/>
                <entry key="mapKey_02" value="mapVal_02"/>
                <entry key="mapKey_03" value="mapVal_03"/>
            </map>
        </constructor-arg>
        <!--自定義類User-->
        <constructor-arg name="user" ref="user"/>
        <!--Date類-->
        <constructor-arg name="date" value="2019-5-21"/>
    </bean>

    <!--配置User類-->
    <bean id="user" class="per.fei._3SpringProjectTest.User">
        <property name="name" value="張三"/>
        <property name="age" value="22"/>
    </bean>
    <!--自定義屬性編輯器到Spring中-->
    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map >
                <entry key="java.util.Date" value="per.fei._3SpringProjectTest.MyConvert"/>
            </map>
        </property>
    </bean>

</beans>

Test類運行結果:

 

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