1. 程式人生 > >Spring框架-IOC/DI詳細學習

Spring框架-IOC/DI詳細學習

inject 策略 enc 8.4 red 自己 index 項目 構造器

一.IOC/DI概念

參考博客:https://www.cnblogs.com/xdp-gacl/p/4249939.html

IOC(inversion of control, 控制反轉)是一種設計思想,而不是一種技術. IOC意味著將我們設計好的對象交給容器控制,而不是交給對象內部控制.它指導我們設計出松耦合,更優良的程序. 傳統程序都是由在類內部主動創建依賴對象來使用, 從而導致類與類之間高耦合, 難於測試; 有了IOC容器,把創建和查找依賴對象的控制權交給了容器, 由容器進行註入組合對象,所以對象和對象之間是松耦合, 這樣使得程序的整個體系結構非常靈活.

實際上,IOC可以看做是一個大型中介, 將對象的信息登記其中,這個對象是什麽, 這個對象又需要什麽 ,由中介公司給對象分配它需要的資源, 以及將對象分配給需要它的對象.Spring在這個過程中控制著對象的生命周期和對象間的關系, 所有類的創建和銷毀都由Spring控制, 而不是需要它們的類控制, 這就是控制反轉.

DI(Dependency Injection, 依賴註入)是IOC的一個重點,它實現IOC的一種方法,或者說DI是IOC的另一種說法 , 它負責實現IOC動態地向某個對象提供它所需要的其他對象. 比如一個人需要一根掃把, 不是由這個人自己去做掃把, 而是由Spring將做好的掃把給他, 而這個人全程需要做的,就是喊我要一根掃把. 什麽叫做依賴, 人需要掃把, 人就對掃把有了依賴. 而DI就是根據這個依賴,將掃把分配給這個人的.

二.IOC的使用

參考博客:https://www.cnblogs.com/best/p/5727935.html#_lab2_0_4

IOC實現的兩種方式: XML配置

, Spring註解

依賴註入的兩種形式: Setter依賴註入, 構造方法依賴註入

1.XML配置的兩種依賴註入形式:

(1)Setter依賴註入: 首先創建一個Book類,將其屬性設置好setter,getter方法

public class Book {
    private int id;
    private String name;
    private User user;

    public int getId() {
        return id;
    }

    public void setId(int
id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } }

然後在applicationContext.xml中添加bean, 告訴Spring兩者的依賴關系,實現註入

    <bean name="book" class="pojo.Book">
        <property name="name" value="book1"></property>
        <property name="user" ref="user"></property>   <!--這裏將book依賴了user,因此在我們獲取book時,會獲取指定的user對象 -->
    </bean>

    <bean name="user" class="pojo.User">
        <property name="name" value="user1"></property>
        <property name="age" value="12"></property>
    </bean>

(2)構造方法依賴註入:

我們將BOOK類中添加了一個兩個參數的構造方法,然後在xml文件中確定兩個參數的值,從而讓spring容器能夠創造對象

public class Book {
    private int id;
    private String name;
    private User user;

    //Constructor
    public Book(String name,User user){
        this.name = name;
        this.user = user;
    }
}

xml文件中的配置為: 指定了構造器有兩個參數, 第一個參數是String , 第二個參數指向了另一個bean

    <bean name="book" class="pojo.Book">
        <constructor-arg type="java.lang.String">   <!-- 指定參數的類型,可以在有多個構造器時不產生歧義 -->
            <value>Adel</value>
        </constructor-arg>
        <constructor-arg>
            <ref bean="user"></ref>
        </constructor-arg>
    </bean>

    <bean name="user" class="pojo.User">
        <property name="name" value="user1"></property>
        <property name="age" value="12"></property>
    </bean>

或者可以這樣寫構造參數:

    <bean name="book" class="pojo.Book">
        <constructor-arg name="name" value="lala"></constructor-arg>
        <constructor-arg name="user" ref="user"></constructor-arg>
    </bean>

又或者這樣寫:

    <bean name="book" class="pojo.Book">
        <constructor-arg index="0" value="lala"></constructor-arg>
        <constructor-arg index="1" ref="user"></constructor-arg>
    </bean>

(3) Scope對象作用域: 從Spring容器中獲取的對象的作用域可以修改,默認的作用域為單例, 具體如下

  • Singleton : 每個Spring容器作用域中,一個bean定義 只對應一個對象實例
  • Prototype: 一個bean定義對應多個對象實例
  • Request: 一個bean定義作用於HTTP request 生命周期, 指每個HTTP request 從Spring獲取的對象實例不一樣. 僅在基於web的Spring ApplicationContext中有效.
  • Session: 一個bean定義作用域HTTP session 生命周期,
  • Global Session: 一個bean定義作用域全局的HTTP session 生命周期.
  • Application: 一個bean定義作用於整個ServletContext生命周期

其在配置中寫在bean的屬性Scope中:

    <bean name="book" class="pojo.Book" scope="singleton">
        <constructor-arg index="0" value="lala"></constructor-arg>
        <constructor-arg index="1" ref="user"></constructor-arg>
    </bean>

(4)延遲初始化bean: 通常ApplicationContext默認在啟動時將所有singleton bean提前實例化. 通常提前實例化是好事,避免某些錯誤因為延遲實例化而不被提前發現. 但是如果想防止提前實例化, 比如你只需要用到book對象, 而不需要它初始化它的user對象, 就可以用lazy-init屬性來控制

    <bean name="book" class="pojo.Book" >
        <constructor-arg index="0" value="lala"></constructor-arg>
        <constructor-arg index="1" ref="user"></constructor-arg>
    </bean>

    <bean name="user" class="pojo.User" lazy-init="true">
        <property name="name" value="user1"></property>
        <property name="age" value="12"></property>
    </bean>

(5)回調方法: 初始化回調函數init

public class Book {
    private int id;
    private String name;
    private User user;

//    Constructor
    public Book(String name,User user){
        this.name = name;
        this.user = user;
    }

    public void init(){
        System.out.println("創建了一本書");
    }

    public void destroy(){
        System.out.println("銷毀了一本書");
    }
}

在xml文件中設置初始化回調函數,就會在創建時調用

    <bean name="book" class="pojo.Book" init-method="init" destroy-method="destroy" >
        <constructor-arg index="0" value="lala"></constructor-arg>
        <constructor-arg index="1" ref="user"></constructor-arg>
    </bean>

2.Spring註解:

註解會削減一些配置的工作量, 但是代碼的耦合度會增加, 應當根據需要使用註解

(1)@Component註解: 這個註解和<bean>作用類似, 通過增加@Component()註解中的參數,指定其在Spring容器中的名稱. 如果不寫,則是默認其在Spring容器中的名稱為類名的小寫 如User類 在Spring容器中默認名稱為 user

@Component("user")
public class User {
    private int id;
    private int age;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

}

在配置文件中,即使沒有user這個bean , 其它bean也可以用到

    <bean name="book" class="pojo.Book" init-method="init" destroy-method="destroy" >
        <constructor-arg index="0" value="lala"></constructor-arg>
        <constructor-arg index="1" ref="user"></constructor-arg>
    </bean>

    <!--<bean name="user" class="pojo.User" lazy-init="true">-->
        <!--<property name="name" value="user1"></property>-->
        <!--<property name="age" value="12"></property>-->
    <!--</bean>-->

(2)@Auotowired 註解: 在Spring中,可以使用 @Autowired 註解通過setter方法構造函數字段自動裝配Bean。

通過setter方法
public class Book {
    private int id;
    private String name;
    private User user;

    public User getUser() {
        return user;
    }

    @Autowired
    public void setUser(User user) {
        this.user = user;
    }
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

}

這樣,即使在applicationContext.xml文件中沒有設置要在book中user依賴哪個對象, Spring容器也會自動裝配

    <bean name="book" class="pojo.Book"  >
        <property name="name" value="lala"></property>
        <property name="id" value="12"></property>
    </bean>

    <bean name="user" class="pojo.User" lazy-init="true">
        <property name="name" value="user1"></property>
        <property name="age" value="12"></property>
    </bean>

通過構造函數:

public class Book {
    private int id;
    private String name;
    private User user;

//    Constructor
    @Autowired
    public Book(User user){
        this.user = user;
    }
}

通過字段:

public class Book {
    private int id;
    private String name;
    @Autowired
    private User user;
}

(3)@Resource註解 : 這個註解與@Autowired不同是, 它可以指定裝配對象的名稱,或者類型,或者兩者都指定

如book和user

@Component("u")
public class User {
    private int id;
    private int age;
    private String name;
//setter 和 getter
}
@Component("b")
public class Book {
    private int id;
    private String name;
    @Resource(name = "u")
    private User user;
//setter和getter
}
@Component("b")
public class Book {
    private int id;
    private String name;
    @Resource(name = "u",type = User.class)
    private User user;
}

或者這樣

@Component("b")
public class Book {
    private int id;
    private String name;
    @Resource(name = "u",type = User.class)
    private User user;
各種註解的解釋, 以下轉自 https://www.cnblogs.com/best/p/5727935.html#_lab2_0_4

@Service用於註解業務層組件(我們通常定義的service層就用這個)

@Controller用於註解控制層組件(如struts中的action)

@Repository用於註解數據訪問組件,即DAO組件

@Component泛指組件,當組件不好歸類的時候,我們可以使用這個註解進行註解。

裝配註解主要有:@Autowired、@Qualifier、@Resource,它們的特點是:

1、@Resource默認是按照名稱來裝配註入的,只有當找不到與名稱匹配的bean才會按照類型來裝配註入;

2、@Autowired默認是按照類型裝配註入的,如果想按照名稱來轉配註入,則需要結合@Qualifier一起使用;

3、@Resource註解是由J2EE提供,而@Autowired是由spring提供,故減少系統對spring的依賴建議使用@Resource的方式;如果Maven項目是1.5的JRE則需換成更高版本的。

4、@Resource和@Autowired都可以書寫註解在字段或者該字段的setter方法之上

5、@Autowired 可以對成員變量、方法以及構造函數進行註釋,而 @Qualifier 的註解對象是成員變量、方法入參、構造函數入參。

6、@Qualifier("XXX") 中的 XX是 Bean 的名稱,所以 @Autowired 和 @Qualifier 結合使用時,自動註入的策略就從 byType 轉變成 byName 了。

7、@Autowired 註釋進行自動註入時,Spring 容器中匹配的候選 Bean 數目必須有且僅有一個,通過屬性required可以設置非必要。

8、@Resource裝配順序
  8.1. 如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則拋出異常
  8.2. 如果指定了name,則從上下文中查找名稱(id)匹配的bean進行裝配,找不到則拋出異常
  8.3. 如果指定了type,則從上下文中找到類型匹配的唯一bean進行裝配,找不到或者找到多個,都會拋出異常
  8.4. 如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有匹配,則回退為一個原始類型進行匹配,如果匹配則自動裝配;

Spring框架-IOC/DI詳細學習