Spring Ioc (Inversion of Control)

如下整理自http://jinnianshilongnian.iteye.com/blog/1413846,若有侵權當即刪除。

Iochtml

Ioc(Inversion of Control),即控制反轉。不是什麼技術,而是一種設計思想。在Java開發中,Ioc意味着將你設計好的對象交給容器控制,而不是傳統的在你的對象內部直接控制。 傳統Java SE程序設計,咱們直接在對象內部經過new進行建立對象,是程序主動去建立依賴對象;而IoC是有專門一個容器來建立這些對象,即由Ioc容器來控制對象的建立;IoC 容器控制了對象(主要控制了外部資源獲取,不僅是對象包括好比文件等)。
傳統應用程序是由咱們本身在對象中主動控制去直接獲取依賴對象,也就是正轉;而反轉則是由容器來幫忙建立及注入依賴對象;爲什麼是反轉?由於由容器幫咱們查找及注入依賴對象,對象只是被動的接受依賴對象,因此是反轉;哪些方面反轉了?依賴對象的獲取被反轉了。

Ioc 容器java

IoC容器就是具備依賴注入功能的容器,IoC容器負責實例化、定位、配置應用程序中的對象及創建這些對象間的依賴。應用程序無需直接在代碼中new相關的對象,應用程序由IoC容器進行組裝。在Spring中BeanFactory是IoC容器的實際表明者web

Spring IoC容器如何知道哪些是它管理的對象呢?這就須要配置文件,Spring IoC容器經過讀取配置文件中的配置元數據,經過元數據對應用中的各個對象進行實例化及裝配。通常使用基於xml配置文件進行配置元數據,並且Spring與配置文件徹底解耦的,可使用其餘任何可能的方式進行配置元數據,好比註解、基於java文件的、基於屬性文件的配置均可以。

Spring Ioc容器的表明就是org.springframework.beans包中的BeanFactory接口,BeanFactory接口提供了IoC容器最基本功能;而org.springframework.context包下的ApplicationContext接口擴展了BeanFactory,還提供了與Spring AOP集成、國際化處理、事件傳播及提供不一樣層次的context實現 (如針對web應用的WebApplicationContext)。簡單說, BeanFactory提供了IoC容器最基本功能,而 ApplicationContext 則增長了更多支持企業級功能支持。ApplicationContext徹底繼承BeanFactory,於是BeanFactory所具備的語義也適用於ApplicationContext。

容器實現一覽:

• XmlBeanFactory:BeanFactory實現,提供基本的IoC容器功能,能夠從classpath或文件系統等獲取資源;
(1) File file = new File("fileSystemConfig.xml");
Resource resource = new FileSystemResource(file);
BeanFactory beanFactory = new XmlBeanFactory(resource);spring

(2)Resource resource = new ClassPathResource("classpath.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
Deprecated. as of Spring 3.1 in favor of DefaultListableBeanFactory and XmlBeanDefinitionReader編程

• ClassPathXmlApplicationContext:ApplicationContext實現,從classpath獲取配置文件;
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath.xml");數組

• FileSystemXmlApplicationContext:ApplicationContext實現,從文件系統獲取配置文件。
BeanFactory beanFactory = new FileSystemXmlApplicationContext("fileSystemConfig.xml");
ide

ApplicationContext接口獲取Bean方法簡介:測試

Object getBean(String name) 根據名稱返回一個Bean,客戶端須要本身進行類型轉換
T getBean(String name, Class<T> requiredType) 根據名稱和指定的類型返回一個Bean,客戶端無需本身進行類型轉換,若是類型轉換失敗,容器拋出異常;
T getBean(Class<T> requiredType) 根據指定的類型返回一個Bean,客戶端無需本身進行類型轉換,若是沒有或有多於一個Bean存在容器將拋出異常;
Map<String, T> getBeansOfType(Class<T> type) 根據指定的類型返回一個鍵值爲名字和值爲Bean對象的 Map,若是沒有Bean對象存在則返回空的Map。

示例:
項目結構圖,注意fileSystemConfig.xml的位置。用於文件系統配置的讀取
QQ20120731010934_thumb5ui

HelloApi.java和HelloImpl.java是一個簡單的接口及其實現,打印一句Hello World!,bean.xml和fileSystemConfig.xml內容相同,都只有一句簡單的對bean的配置:<bean id="hello" class="cn.nevo.service.impl.HelloImpl"></bean>

測試代碼演示了使用不一樣的Ioc容器實現去讀取類路徑配置文件和文件系統文件的方式:
this

public class HelloServiceTest {

	@SuppressWarnings("deprecation")
	@Test
	public void testXmlBeanFactoryBaseOnFileSystem() {
		// 1.準備配置文件,從文件系統獲取配置文件,默認是相對路徑,能夠指定絕對路徑
		File file = new File("fileSystemConfig.xml");
		Resource resource = new FileSystemResource(file);
		// 2.初始化容器
		BeanFactory beanFactory = new XmlBeanFactory(resource);
		// 二、從容器中獲取Bean
		HelloApi helloApi = beanFactory.getBean("hello", HelloApi.class);
		// 三、執行業務邏輯
		helloApi.sayHello();
	}

	@SuppressWarnings("deprecation")
	@Test
	public void testXmlBeanFactoryBaseOnClassPath() {
		// 1.準備配置文件,從當前類加載路徑中獲取配置文件
		Resource resource = new ClassPathResource("bean.xml");
		// 2.初始化容器
		BeanFactory beanFactory = new XmlBeanFactory(resource);
		// 二、從容器中獲取Bean
		HelloApi helloApi = beanFactory.getBean("hello", HelloApi.class);
		// 三、執行業務邏輯
		helloApi.sayHello();
	}

	@Test
	public void testClassPathXmlApplicationContextBaseOnClassPath() {
		// 1.準備配置文件,從當前類加載路徑中獲取配置文件
		// 2.初始化容器
		BeanFactory beanFactory = new ClassPathXmlApplicationContext("bean.xml");
		// 二、從容器中獲取Bean
		HelloApi helloApi = beanFactory.getBean("hello", HelloApi.class);
		// 三、執行業務邏輯
		helloApi.sayHello();
	}

	@Test
	public void testFileSystemApplicationContextBaseOnFileSystem() {
		// 1.準備配置文件,從文件系統獲取配置文件,默認是相對路徑,能夠指定絕對路徑
		// 2.初始化容器
		BeanFactory beanFactory = new FileSystemXmlApplicationContext(
				"fileSystemConfig.xml");
		// 二、從容器中獲取Bean
		HelloApi helloApi = beanFactory.getBean("hello", HelloApi.class);
		// 三、執行業務邏輯
		helloApi.sayHello();
	}
}

IoC容器究竟是如何工做。在此咱們以xml配置方式來分析一下:
1、準備配置文件:在配置文件中聲明Bean定義也就是爲Bean配置元數據。
2、由IoC容器進行解析元數據: IoC容器的Bean Reader讀取並解析配置文件,根據定義生成BeanDefinition配置元數據對象,IoC容器根據BeanDefinition進行實例化、配置及組裝Bean。
3、實例化IoC容器:也就是實例化BeanFactory的各類實現,由客戶端實例化容器,獲取須要的Bean。

XML配置文件的結構

<beans>  
    <import resource=」resource1.xml」/>  
    <bean id=」bean1」class=」」></bean>  
    <bean id=」bean2」class=」」></bean>  
    <bean name=」bean2」class=」」></bean>  
    <alias alias="bean3" name="bean2"/>  
    <import resource=」resource2.xml」/>  
</beans>

一、<bean>標籤主要用來進行Bean定義;
二、alias用於定義Bean別名的;
三、import用於導入其餘配置文件的Bean定義,這是爲了加載多個配置文件,固然也能夠把這些配置文件構造爲一個數組(new String[] {「config1.xml」, config2.xml})傳給ApplicationContext實現進行加載多個配置文件,那一個更適合由用戶決定;這兩種方式都是經過調用Bean Definition Reader 讀取Bean定義,內部實現沒有任何區別。<import>標籤能夠放在<beans>下的任何位置,沒有順序關係。

Bean

由IoC容器管理的那些組成你應用程序的對象咱們就叫它Bean, Bean就是由Spring容器初始化、裝配及管理的對象,除此以外,bean就與應用程序中的其餘對象沒有什麼區別了。那IoC怎樣肯定如何實例化Bean、管理Bean之間的依賴關係以及管理Bean呢?這就須要配置元數據,在Spring中由BeanDefinition表明,後邊會詳細介紹,配置元數據指定如何實例化Bean、如何組裝Bean等。

Bean的配置

Spring IoC容器目的就是管理Bean,這些Bean將根據配置文件中的Bean定義進行建立,而Bean定義在容器內部由BeanDefinition對象表示,該定義主要包含如下信息:
全限定類名(FQN):用於定義Bean的實現類;
Bean行爲定義:這些定義了Bean在容器中的行爲;包括做用域(單例、原型建立)、是否惰性初始化及生命週期等;
Bean建立方式定義:說明是經過構造器仍是工廠方法建立Bean;
Bean之間關係定義:即對其餘bean的引用,也就是依賴關係定義,這些引用bean也能夠稱之爲同事bean 或依賴bean,也就是依賴注入。

Bean定義只有「全限定類名」在當使用構造器或靜態工廠方法進行實例化bean時是必須的,其餘都是可選的定義。難道Spring只能經過配置方式來建立Bean嗎?回答固然不是,某些SingletonBeanRegistry接口實現類實現也容許將那些非BeanFactory建立的、已有的用戶對象註冊到容器中,這些對象必須是共享的,好比使用DefaultListableBeanFactory 的registerSingleton() 方法。不過建議採用元數據定義。

BeanDefinition

spring能夠經過xml配置文件定義bean,beanFactory能夠建立、查找配置文件中定義的這些bean,spring內部是如何將配置文件中所定義的bean變成可讓beanFactory建立與管理的呢?這是依靠BeanDefinition進行實現。BeanDefinition是一個接口,它描述了一個bean的實例,保存了bean的定義信息,是bean在內存中的描述形式。xml配置文件所定義的每一個bean在內存中都有對應的Bedifinition對象進行描述。BeanFactory在查找,建立及管理bean時,會先查找其在內存中所保存的BeanDefinition,而後再根據BeanDefinition中的bean描述內容建立bean或返回所需的bean信息。

根據面向接口編程的原則,spring定義了接口BeanDefinitionRegistry註冊管理全部的Bedifinition,實現此接口就能夠管理bean的定義,DefaultListableBeanFactory類實現了此接口,所以DefaultListableBeanFactory類及其子類具備管理beanDefinition的功能。

Spring讀取分析配置文件,根據配置文件中的定義,爲這些bean建立對應的BeanDifinition,並將BeanDifinition註冊至beanFactory中(具體表現爲註冊在DefaultListableBeanFactory類型的beanFactory)。當用戶須要使用bean時,將傳入bean的名稱或類型給beanFactory,beanFactory從其所保存的beanDefinition中查找,當找到符合條件的beanDefinition後,則將根據beanDefinition中的bean信息建立bean對象,並返回給用戶。

實例化Bean

Spring IoC容器如何實例化Bean呢?
傳統應用程序能夠經過new和反射方式進行實例化Bean。而Spring IoC容器則須要根據Bean定義裏的配置元數據使用反射機制來建立Bean。在Spring IoC容器中根據Bean定義建立Bean主要有如下幾種方式:
一、使用構造器實例化Bean,這是最簡單的方式,Spring IoC容器即能使用默認空構造器也能使用有參數構造器兩種方式建立Bean
二、使用靜態工廠方式實例化Bean,使用這種方式除了指定必須的class屬性,還要指定factory-method屬性來指定實例化Bean的方法,並且使用靜態工廠方法也容許指定方法參數,spring IoC容器將調用此屬性指定的方法來獲取Bean
三、使用實例工廠方法實例化Bean,使用這種方式不能指定class屬性,此時必須使用factory-bean屬性來指定工廠Bean,factory-method屬性指定實例化Bean的方法,並且使用實例工廠方法容許指定方法參數,方式和使用構造器方式同樣

示例:
一、構造器實例化Bean:

package cn.nevo.service.impl;

import cn.nevo.service.HelloApi;

public class HelloImpl implements HelloApi {
	private String message;
	
	public HelloImpl() {
		this.message = "Hello World!";
	}
	
	public HelloImpl(String message) {
		this.message = message;
	}

    @Override
    public void sayHello() {
        System.out.println(message);
    }
}

bean.xml
<!-- 使用默認構造參數實例化bean -->
  <bean id="hello" class="cn.nevo.service.impl.HelloImpl"></bean>
  
  <!-- 使用有參構造參數實例化bean -->
  <bean id="hello2" class="cn.nevo.service.impl.HelloImpl">
  	<constructor-arg index="0" value="New Hello World!"/>
  </bean>

測試
public class HelloServiceTest {

	@Test
	public void testClassPathXmlApplicationContextBaseOnClassPath() {
		// 1.準備配置文件,從當前類加載路徑中獲取配置文件
		// 2.初始化容器
		BeanFactory beanFactory = new ClassPathXmlApplicationContext("bean.xml");
		// 二、從容器中獲取Bean
		HelloApi helloApi = beanFactory.getBean("hello", HelloApi.class);
		HelloApi helloApi2 = beanFactory.getBean("hello2", HelloApi.class);
		// 三、執行業務邏輯
		helloApi.sayHello();
		helloApi2.sayHello();
	}
}

二、靜態工廠方式實例化Bean
返回HelloImpl實例的靜態工廠類HelloImplStaticFactory.java
package cn.nevo.service.impl.staticfactory;

import cn.nevo.service.HelloApi;
import cn.nevo.service.impl.HelloImpl;

public class HelloImplStaticFactory {
	public static HelloApi getInstance(String message) {
		return new HelloImpl(message);
	}
}

bean.xml
<!-- 使用靜態工廠方法實例化bean -->
  <bean id="statichello" class="cn.nevo.service.impl.staticfactory.HelloImplStaticFactory" factory-method="getInstance">
  	<constructor-arg index="0" value="static factory instance"/>
  </bean>

測試
HelloApi helloApi3 = beanFactory.getBean("statichello", HelloApi.class);
		helloApi3.sayHello();


三、實例工廠方式實例化Bean
用於產生HelloImpl實例的HelloImplInstanceFactory.java
package cn.nevo.service.impl.instancefactory;

import cn.nevo.service.HelloApi;
import cn.nevo.service.impl.HelloImpl;

public class HelloImplInstanceFactory {
	public HelloApi getInstance(String message) {
		return new HelloImpl(message);
	}
}

bean.xml
<!-- 使用實例工廠方法實例化bean -->
  <bean id="instancehello" class="cn.nevo.service.impl.instancefactory.HelloImplInstanceFactory"/>
  <bean id="instance" factory-bean="instancehello" factory-method="getInstance">
  	<constructor-arg index="0" value="instance factory"/>
  </bean>

測試

HelloApi helloApi4 = beanFactory.getBean("instance", HelloApi.class);
		helloApi4.sayHello();
相關文章
相關標籤/搜索