Spring框架之IOC&DI

前言:

經過一系列被鄙視後,終於拿到一份還算滿意的小offer,以後的日子就是不斷學習的過程了,加油。

 

最近開始學習Spring框架,現將自己目前的學習狀況以及自己的一部分理解總結一下。

 

Spring框架提供給用戶的有兩個大的主要功能:控制反轉(IOC)或依賴注入(DI)和麪向切面編程(AOP),當然還提供了其他的比如操作數據庫的模板化以及事務管理、web開發中的Spring MVC等,這些以後會單獨總結,但是最主要的還是依賴注入和麪向切面編程。

一、Spring IOC以及DI的必要性

本篇文章總結控制反轉(IOC)和依賴注入(DI),兩者共同解決一個問題:對象耦合問題。下面舉例說明。

 

在我們訪問數據庫時會寫DAO,假設現在需要針對不同的數據庫(mysql或oracle數據庫)來寫DAO,於是會有一個DAO接口類,並且會有針對不同數據庫的相應的實現類(MysqlDaoImpl 和 OracleDaoImpl),如下所示:

DAO接口類:

 

package com.springframework.ioc;

public interface MyDao{
	//添加
	public void addUser();
}

 mysql實現類:

 

 

package com.springframework.ioc;

public class MysqlDaoImpl implements MyDao{
	
	@Override
	public void addUser(){
		System.out.println("mysql add user...");
	}
}

 oracle實現類:

 

 

package com.springframework.ioc;

public class OracleDaoImpl implements MyDao{
	
	@Override
	public void addUser(){
		System.out.println("oracle add user...");
	}
}

 這時上層service層要調用DAO,假設我的寫法如下:

 

 

package com.springframework.ioc;

public class MyService{
	
	private MyDao myDao;
	
	public MyService(){
		myDao = new MysqlDaoImpl();
	}
	
	public void addUser(){
		myDao.addUser();
	}
}
<constructor-arg ref = "oracle"/>

 這種寫法的缺點就是當我要換用作oracle數據庫時需要修改MyService類,將myDao的創建改爲

myDao = new OracleDaoImpl();

 不符合開放-封閉原則, 於是我修改我的代碼如下:

 

 

package com.springframework.ioc;

public class MyService{
	
	private MyDao myDao;
	
	public MyService(MyDao myDao){
		this.myDao = myDao;
	}
	
	public void addUser(){
		myDao.addUser();
	}
}

 這樣第三方在調用service層的addUser方法時的代碼如下:

 

 

package com.springframework.ioc;

public class Test{
	public static void main(String[] args){

		MyService myService = new MyService(new MysqlDaoImpl());
		myService.addUser();	
	}
}

 這樣當第三方需要的是oracle數據庫的操作時,只需要將自己本身的代碼修改爲:

 

 

MyService myService = new MyService(new OracleDaoImpl());

 

 

將myDao對象的具體創建控制權交給第三方,而另外兩房不需要關心,這就叫做控制反轉。

 

以上是傳統的編程做法,其實Spring框架也同樣爲我們解決了以上問題,使用的方式就是依賴注入方式。下面採用Spring框架來實現兩個對象之間的解耦。

 

在Spring框架中,每一個類對象叫做一個bean,每一個bean有一個bean id和具體的實現類,Spring的IOC容器就是對這些bean進行管理和配置,將bean與bean之間的依賴通過配置文件或者註解的方式表現出來。

 

還是以以上的例子爲例,爲在xml文件中配置bean並將bean和bean之間的依賴進行配置如下:

 

<?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-2.5.xsd
			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"
			default-lazy-init="true">
			
			<!-- beans declare go here -->
			<!-- 通過默認構造器注入 -->
			<bean id="mysql" class="com.springframework.ioc.MysqlDaoImpl"/>
			<bean id="oracle" class="com.springframework.ioc.OracleDaoImpl"/>
			<!-- 通過含參構造器注入 -->
			<bean id = "myservice" class="com.springframework.ioc.MyService">
				<constructor-arg ref = "mysql"/>
			</bean>
	</beans>

 這樣就將每個類作爲bean注入到spring 的IOC容器中,然後第三方就可以獲取bean並實例化bean,並調用其方法。如下所示:

 

 

package com.springframework.ioc;

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

public class Test{
	public static void main(String[] args){

		//傳統做法
		/*MyService myService = new MyService(new OracleDaoImpl());
		myService.addUser();	*/
		
		//使用spring後的做法
		ApplicationContext context = new ClassPathXmlApplicationContext("ioc.xml");
		MyService myService = (MyService)context.getBean("myservice");
		myService.addUser();
	}
}

 第三方如果想要調用的是oracle,則只需修改xml配置文件將

 

 

<constructor-arg ref = "mysql"/>

 修改爲:

 

<constructor-arg ref = "oracle"/>

 即可。

將一個類通過xml配置文件的形式注入到另一個類中,實現了兩個類之間的依賴,並且實現瞭解耦。兩個類只需要關注自己的業務邏輯實現,無需管理其它類的實現。

 

二、Spring IOC&DI的實現原理

 

Spring管理bean和bean之間的依賴管理分別對應於對象的控制反轉和依賴注入,控制反轉即spring將xml文件進行解析,包括bean的id、class名字、別名、property屬性等,解析爲一個個的bean註冊到IOC容器中;依賴注入即將bean通過反射的方式實例化bean,並通過三種注入方式將其他的bean注入進來,然後返回給第三方。

 

比如對於一個簡單配置的spring 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-2.5.xsd
			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
			
			<!-- beans declare go here -->
			<!-- 通過默認構造器注入 -->
			<bean id="mysql" class="com.springframework.ioc.MysqlDaoImpl"/>
			<bean id="oracle" class="com.springframework.ioc.OracleDaoImpl"/>
	</beans>

 當Spring框架啓動時,會對xml文件進行解析,按照節點首先解析的是

<bean id="mysql" class="com.springframework.ioc.MysqlDaoImpl"/>

在Spring框架中維護一個BeanDefinition來存儲每一個bean節點的屬性,如id、classname、propertity、autowire mode、initmethod name 、destroymethod name 等。

如下所示:

class BeanDefinition{
	//bean 的id
	private String id;
	
	//bean 的classname
	private String className;
	//bean 的init method name
        private String initMethodName;
        //等等...
}

 每有一個BeanDefinition即有一個相應的對象,內部裝載着以上介紹的bean的各種屬性(id、className等),然後將這些BeanDefinition對象裝載到IOC容器中,內部爲鍵值爲nbeanName,值爲BeanDefinition的map表,如下所示:

/** Map of bean definition objects, keyed by bean name */
	private final Map<String, BeanDefinition> beanDefinitionMap = 
                                          new ConcurrentHashMap<String, BeanDefinition>(64);

這就完成了對xml文件的解析以及對BeanDefinition實例的註冊。

當調用

		MyService myService = (MyService)context.getBean("myservice");

 時,

即爲從map表中根據鍵值beanname(即bean的id)取出BeanDefinition實例,然後通過工廠方式或者構造函數反射機制創建bean實例,然後對beandefinition中的property屬性進行解析,並通過set方法或者默認構造函數實現其他bean實例的注入。

這樣就將每一個類以及類與類之間的依賴以bean的形式交給Spring的IOC容器來管理。

詳細的IOC&DI的源代碼解析或者原理解析可以參考這篇文章 http://www.cnblogs.com/ITtangtang/p/3978349.html。

 

參考:
http://www.importnew.com/13619.html

http://www.cnblogs.com/xdp-gacl/p/4249939.html

www.cnblogs.com/ITtangtang/p/3978349.html

 

 

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