Spring回顧(一)IoC & DI

什麼是IoC?何爲DI?

IoC是Sprnig框架實現的一種代替程序員,管理Java類的模式。IoC全稱爲Inversion of Control,即控制反轉。在Spring項目中,將Java類的控制權交給了Spring的IoC容器。類與類之間的泛化關係、依賴關係都能通過Spring的配置來實現,而不需要手動編寫。

DI,全稱爲Dependency Injection,即依賴注入。在Spring通過配置文件配置了依賴關係之後,可以通過多種方式對對象進行注入,比如說Service實現類中的方法需要依賴dao實現類,我們就能通過Spring配置文件寫好注入配置,在使用時,我們只需要定義好dao接口,而不需要去獲取它的實例,由Spring爲我們注入。


能通過Spring的IoC容器和DI做什麼?

1. IoC(控制反轉)

控制反轉,能讓spring容器爲我們創建對象。簡單來說,我們只需要準備好Object object
Spring能幫我們實現餘下的new Object()部分。在Spring中,每個被放入容器的類被成爲bean,ApplicationContext代表了IoC容器,我們可以通過這個類來讀取配置文件,根據配置文件實例化bean。下面看看如何通過配置實現控制反轉

1.1 配置文件

配置文件一般放在class目錄下,即開發中的src目錄。配置文件的主要結構如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <!-- 
          beans  存放了很多個類
          把一個類放入到spring容器中,該類就是bean
     -->
     <!-- 
          一個bean就是描述一個類
          id就是標示符
          命名規範:類的第一個字母變成小寫,其他的字母保持不變
          class爲類的全名
      -->
     <bean id="helloWorld" class="com.spring.ioc.HelloWorld"></bean>
</beans>

1.2 ApplicationContext 的使用

ApplicationContext在Spring中代表一個IoC容器,ApplicationContext只是一個接口,在這裏我用ClassPathXmlApplicationContext這個實現類來直接獲取配置文件。其中在容器中對象的創建又有三種方法:根據默認構造方法創建、靜態工廠方法創建、實例工廠方法創建

默認構造方法創建

springIoc容器默認情況下會根據bean的構造函數來實例化對象。所以在bean的配置中我們可以添加參數,參數可以是變量也可以是另一個bean的引用,如果是bean的引用,則使用ref=”bean_id”來關聯。
配置方式applicationContext.xml

<bean id="helloWorld" class="com.spring.ioc.HelloWorld"></bean>

HelloWorld.java

package com.spring.ioc;

public class HelloWorld {
    public HelloWorld(){
        System.out.println("create a HelloWorld!");
    }
    public void hello(String name){
        System.out.println("Hello," + name);
    }
}

IoCTest.java

package com.spring.ioc;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class IoCTest {
    @Test
    public void testIoC(){
        //讀取配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //通過getBean方法得到
        HelloWorld helloWorld = (HelloWorld) context.getBean("helloWorld");
        helloWorld.hello("Cindy");
    }
}
靜態工廠方法創建對象

可以在配置bean的時候可以配置使用靜態工廠來實例化對象。Spring容器會在內部調用靜態工廠的實例化方法來實例化對象。
配置方式:applicationContext.xml

<bean id="helloWorld" class="com.spring.ioc.HelloWorldFactory"
           factory-method="getInstance">
    </bean>

靜態工廠HelloWorldFactory.java

package com.spring.ioc;

public class HelloWorldFactory {
    public static HelloWorld getInstance(){
        return new HelloWorld();
    }
}
實例工廠創建對象

使用實例工廠方法實例化對象,在定義bean的時候使用factory-bean屬性和factory-method屬性,來指定實例工廠的實例方法。與靜態工廠方法不同,這裏是先實例化了一個工廠類,然後調用了實例化工廠方法,而靜態工廠方法不實例化工廠類,直接調用其靜態實例化方法。
配置方式:applicationContext.xml

<bean id="helloWorldFactory" class="com.spring.ioc.HelloWorldFactory2"></bean>
<bean id="helloWorld" factory-method="getInstance" factory-bean="helloWorldFactory"></bean>

實例工廠HelloWorldFactory2.java

package com.spring.ioc;

public class HelloWorldFactory2 {
    public HelloWorldFactory2(){
        System.out.println("create a HelloWorldFactory");
    }
    public HelloWorld getInstance(){
        return new HelloWorld();
    }
}

1.3 什麼時候實例化對象?

①加載Spring容器,加載過程中,調用bean的構造函數爲bean創建對象。
②將bean的lazy-init屬性,設置爲true(默認爲false),設置延遲加載,spring容器加載時不實例化,在getBean()的時候才實例化。
一般情況下使用第一種方式,因爲在啓動過程中就能檢測出spring配置文件中包含的錯誤,更加安全。第二種在啓動過程中發現不了配置文件錯誤。如果要加載的bean實在太消耗性能,再考慮使用第二種方法。

1.4 bean的單例模式和多例模式

在配置bean的過程中,可以爲其指定是否使用單例模式實例化對象,默認爲單例模式,即只實例化一次,多次使用。設置的方法爲:
① 多例模式:scope=prototype

<bean id="helloWorld" class="com.spring.ioc.HelloWorld" scope="prototype"></bean>

②單例模式(defalut): scope=singleton

<bean id="helloWorld" class="com.spring.ioc.HelloWorld" scope="singleton"></bean>

1.5 bean的init方法,destory方法

在配置bean的時候,可以爲其指定init()方法和destory()方法,分別在實例化和銷燬的時候執行。其中在容器關閉時,bean銷燬。
配置了之後整個流程可表示爲:構造方法(創建對象)->init方法->其他方法->destory方法

*當scope=prototype的時候,spring容器不會執行destory方法

配製方法 applicationContext.xml :

<bean id="helloWorld" class="com.spring.ioc.HelloWorld" init-method="init" destroy-method="destroy"></bean>

HelloWorld.java

package com.spring.ioc;

public class HelloWorld {
    public HelloWorld(){
        System.out.println("create a HelloWorld!");
    }
    public void hello(String name){
        System.out.println("Hello," + name);
    }
    public void init(){
        System.out.println("init HelloWorld");
    }
    public void destroy(){
        System.out.println("destroy HelloWorld");
    }
}

1.6 bean繼承關係

在spring容器中使用parent屬性指定父類,實現繼承。例子:

package com.spring.xml.extend;
public class Animal {
    public void walk(){
        System.out.println("Animal walking");
    }
}
package com.spring.xml.extend;
public class Dog extends Animal {
}

配置:

<bean class="com.spring.xml.extend.Animal" id="animal"></bean>
<bean class="com.spring.xml.extend.Dog" id="dog" parent="animal"></bean>

2. DI(依賴注入)

2.1 setter方式注入

配置bean的時候,我們可以在配置文件中指定實例化時,要注入的屬性值。這些值可以是基本數據類型、引用類型、集合類型、或者Propertie,只要在bean的類定義中提供setter方法就能通過配置注入,配置的方法如下:
applicationContext.xml

<bean id="person" class="com.spring.di.Person">
    <!--
        property描述的就是bean中的屬性
          name屬性就是描述屬性的名稱
          value就是值   如果是基本屬性(String),就用value賦值
          ref  如果是引用類型,用ref賦值
     -->
    <property name="pid" value="1"></property>
    <property name="name" value="Cindy"></property>
    <!-- 注入bean -->
    <property name="student" ref="student"></property>
    <property name="lists">
        <list>
            <value>list1</value>
            <value>list2</value>
            <!--
                list中存放一個student對象
             -->
            <ref bean="student"/>
        </list>
    </property>
    <property name="objects">
        <list>
            <value>obj1</value>
            <ref bean="student"/>
        </list>
    </property>
    <property name="sets">
        <set>
            <value>set1</value>
            <ref bean="student"/>
        </set>
    </property>
    <property name="map">
        <map>
            <entry key="m1">
                <value>m1</value>
            </entry>
            <entry key="m2">
                <ref bean="student"/>
            </entry>
        </map>
    </property>
    <property name="properties">
        <props>
            <prop key="p1">p1</prop>
            <prop key="p2">p2</prop>
        </props>
    </property>
</bean>
<bean id="student" class="com.spring.di.Student"
      scope="prototype"></bean>

Student.java

package com.spring.di;

public class Student {
    public Student(){
        System.out.println("student");
    }
    public void say(){
        System.out.println("student");
    }
}

Person.java

public class Person {
    private Long pid;
    private String name;
    private Student student;
    private List lists;
    private Set sets;
    private Map map;
    private Object[] objects;
    private Properties properties;

    public Person(){
        System.out.println("person");
    }
    //...
    //省略setter方法
}

2.2 Constructor 注入

除了調用setter方法注入,還能通過類的構造器來注入參數,同樣是從配置文件中對bean進行配置。首先在類中定義好構造器方法,然後在配置文件中設置參數。
配置方法:
applicationContext.xml

<bean id="person1" class="com.spring.di.Person1">
    <constructor-arg value="Cindy" index="0"></constructor-arg>
    <constructor-arg ref="student" index="1"></constructor-arg>
</bean>

Person1.java

package com.spring.di;

public class Person1 {
    private String name;
    private Student student;

    public Person1(String name, Student student) {
        this.name = name;
        this.student = student;
    }
}

IoC 和 DI 帶來的好處(優勢)

讓客戶端實現完全面向接口編程,在編寫客戶端的時候,不用關心實現類爲什麼具體類型,只用接口類型來完成工作,接口需要具體爲何種類型的實例,由配置文件決定。舉個例子,在一個struts2的aciton中,用接口定義了一個service,而這個service通過spring注入,那麼開發action的人員就不需要管service具體是如何實現的,把編寫service的工作分離出去,方便團隊開發,避免編寫過程中的衝突。

Spring是如何實現IoC和DI的

瞭解Spring框架底層是如何實現控制反轉和依賴注入,需要深入研究源碼。因爲我還沒有深入研究過,只知道大概是使用了Java的反射機制,來操作類。有待自己深入研究。

發佈了58 篇原創文章 · 獲贊 8 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章