天天看點

IOC原理與實踐

前言

什麼是IOC?

IOC(Inversion of Control),即“控制反轉”,是一種代碼設計思想。

在傳統的程式開發中,如果在一個對象中要使用其他的對象,就必須自己手動new一個,而且在使用完之後還需要将對象進行手動銷毀,這樣對象始終會和其他的類藕合起來。

對于Spring架構來說,所謂IOC(控制反轉),就是由Spring來負責控制對象的生命周期和對象之間的關系。所有的類都會在Spring容器中進行登記,告訴Spring我是什麼東西,我需要什麼東西,然後Spring會在系統運作到适當的時候,把你要的東西主動給你,同時也把你交給其他需要你的東西。所有的類的建立、銷毀都由Spring來控制,也就是說控制對象生存周期的不再是引用它的對象,而是Spring容器。

IoC和DI

DI—Dependency Injection,即“依賴注入” : 元件之間依賴關系 由容器在運作期決定,形象的說,即

由容器動态的将某個依賴關系注入到元件之中 。

依賴注入的目的并非為軟體系統帶來更多功能,而是為了提升元件重用的頻率,并為系統搭建一個靈活、可擴充的平台。

IoC和DI 由什麼 關系 呢?其實它們 是同一個概念的不同角度描述,相對IoC 而言,“依賴注入”明确描述了“被注入對象依賴IoC容器配置依賴對象”。IOC主要針對于控制對象的所有權,而DI側重于描述對象的使用的權。

SpringIOC底層實作原理

IOC的底層原理:xml解析,工廠模式,反射

IOC 是基于 IOC 容器完成,IOC 容器底層就是對象工廠

Spring提供IOC容器實作兩種方式:

  • BeanFactory:IOC容器基本實作,是Spring内部的使用接口,不提供開發人員使用 ,加載配置檔案的時候不會建立對象,擷取(使用)對象的時候才會建立對象
  • ApplicationContext:BeanFactory的子接口,提供了更多更強大的功能,一般由開發人員進行使用

    *加載配置檔案的時候就會建立配置檔案的對象。

詳細步驟:

  1. 利用傳入的參數擷取xml檔案的流,并且利用dom4j解析成Document對象。
  2. 對于Document對象擷取根元素對象後對下面的标簽進行周遊,判斷是否有符合的beanId。
  3. 如果找到對應的beanId,相當于找到了一個Element元素,開始建立對象,先擷取class屬性,然後根據屬性值利用反射建立對象。
  4. 周遊标簽下的property标簽,并對屬性指派。注意,需要單獨處理int,float類型的屬性,因為在xml配置中這些屬性都是以字元串的形式來配置的,是以需要進行額外的處理。
  5. 如果屬性property标簽有ref屬性,說明某個屬性的值是一個對象,那麼根據beanId(ref屬性的值)去擷取ref對應的對象,再給屬性指派。
  6. 傳回建立的對象,如果沒有對應的beanId或者下沒有子标簽都會傳回null。

基于xml實作IOC容器

1.建立一個maven項目

引入pom依賴:

<!--用于解析xml -->
        <dependency>
           <groupId>dom4j</groupId>
           <artifactId>dom4j</artifactId>
           <version>1.6.1</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.0</version>
        </dependency>
           

2.建立一個控制對象

@Data
@ToString
public class User {
    private String userId;
    private String userName;
}
           

3.配置xml檔案

<?xml version="1.0" encoding="UTF-8"?>
<beans>
	<bean id="user1" class="zzuli.edu.cn.entity.User">
		<property name="userId" value="0001"></property>
		<property name="userName" value="張三"></property>
	</bean>
	<bean id="user2" class="zzuli.edu.cn.entity.User">
		<property name="userId" value="0002"></property>
		<property name="userName" value="李四"></property>
	</bean>
</beans>
           

4.建立IOC容器類

public class ClassPathXmlApplicationContext {
    //xml配置檔案的路徑
    private String xmlPath;

    public ClassPathXmlApplicationContext(String xmlPath) {
        this.xmlPath = xmlPath;
    }

    /**
     * 擷取Bean對象
     */
    public Object getBean(String beanId) throws Exception {
        // 1.讀取xml配置檔案
        SAXReader saxReader = new SAXReader();
        Document read = saxReader.read(this.getClass().getClassLoader().getResourceAsStream(xmlPath));
        //擷取xml配置檔案的根節點對象(<beans></beans>)
        Element rootElement = read.getRootElement();
        //擷取根節點中所有的子節點對象,也就是所有bean對象(<bean></bean>)
        List<Element> beanElements = rootElement.elements();
        Object obj = null;
        for (Element beanElement : beanElements) {
            // 2.使用beanId查找bean配置,并擷取配置檔案中class的位址(為了與參數beanId區分開,我們命名為beanElementId)
            String beanElementId = beanElement.attributeValue("id");
            //如果不是目前的bean,則跳出本次循環
            if (!beanId.equals(beanElementId)) {
                continue;
            }
            // 擷取bean對應的Class位址
            String beanClassPath = beanElement.attributeValue("class");
            // 3.使用反射執行個體化對象
            Class<?> cls = Class.forName(beanClassPath);
            obj = cls.newInstance();
            // 4.擷取屬性配置,使用反射技術進行指派
            List<Element> fieldElements = beanElement.elements();
            for (Element fieldElement : fieldElements) {
                String name = fieldElement.attributeValue("name");
                String value = fieldElement.attributeValue("value");
                //使用反射api為私有屬性指派
                Field declaredField = cls.getDeclaredField(name);
                //忽略通路權限修飾符的安全檢查,又稱為暴力反射
                declaredField.setAccessible(true);
                declaredField.set(obj, value);
            }
        }
        //傳回bean對象
        return obj;
    }
}

           

5.使用和測試

public class IOCDemo {

    public static void main(String[] args) throws Exception {
        //讀取User的XML配置檔案
        ClassPathXmlApplicationContext appLication = new ClassPathXmlApplicationContext("user.xml");
        //擷取User的Bean對象
        Object bean = appLication.getBean("user1");
        User user = (User) bean;
        System.out.println(user);

        //擷取User的Bean對象
        bean = appLication.getBean("user2");
        user = (User) bean;
        System.out.println(user);
    }
}