Spring:協調作用域不同步的Bean

在整理之前,我們首先應該瞭解Spring支持的五種作用域:

singleton:單例模式,singleton作用域下的Bean將只產生一個實例,因此我們每次獲取的實例都是同一實例。

prototype:每次通過getBean()方法獲取Bean實例時,都會產生一個新的Bean實例,因此每次的實例都不同

request:用於Web應用,對於一次Http請求,request作用域內的Bean只生成一個Bean,也就是說同一次請求內,每次獲取該Bean,

      獲取的都是同一實例,但是如果到了下次請求(刷新頁面)就會再次產生一個Bean實例,但是在每次的請求內多次獲取

      Bean實例都是同一實例。

session:對於一次http會話,session作用域的Bean將只生成一個實例,僅在Web應用中該作用域纔會真的有效。

global session:每個全局的HttpSession對應一個實例

如果不單獨在配置文件中設置,默認的作用域爲singleton作用域。

如果對Bean的作用域還不是很瞭解的可以參考此篇博文:http://blog.csdn.net/vipmao/article/details/51565448


---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

一:普通方法注入

  我們已經知道,singleton是單例模式,該作用域下只產生一個Bean實例,因此我們每次訪問所獲取的都是同一個實例,但是prototype作用域下的Bean,每次訪問都會產生一個新的實例,但是我們試想這麼一個情況:當singleton作用域下的Bean依賴一個prototype作用域下的Bean,這就有意思了,在這種情況下,Spring容器會先創建被依賴的prototype作用域的Bean,然後再創建singleton作用域下的主Bean,然後將prototype Bean注入給singleton Bean完成依賴關係,但是singleton Bean只會創建一次,他的依賴關係也是在創建過程階段完成的,因此只會完成一次依賴關係的創建,這就導致了每次我們通過singleton Bean訪問prototype Bean時,永遠訪問的只是同一個prototype Bean實例,這樣prototype作用域也就喪失了自己原本的作用。

如以下這個例子:

1:chinese Bean實現類 

package com.mao.lookup_method;

public  class Chinese implements Person
{
	private Dog dog;
	public void setDog(Dog dog) {
		this.dog = dog;
	}	
	public Dog getDog() {
		return dog;
	}

	@Override
	public void hunt() 
	{
		// TODO Auto-generated method stub
		System.out.println("我帶着"+getDog()+"出去打獵");
		System.out.println(getDog().run());
	}

}
上面Chinese Bean包含一個hunt()方法,該方法的執行需要依賴Dog的 方法。這裏的Dog是一個接口,我們期望每次執行hunt()方法時可以獲得不同的gunDog Bean,所以後面我們在配置文件beans.xml將gunDog Bean設置成prototype作用域。

2:gunDog Bean的實現類 該類實現Dog接口

package com.mao.lookup_method;

public class GunDog implements Dog
{
	private String name;

	public GunDog() 
	{
	
	}

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

	@Override
	public String run() 
	{
		return"我是一個叫"+name+"的獵犬,奔跑迅速》》》》" ;
		
	}

}

3:配置文件 beans.xml 配置Bean部分

           <bean id="ch" class="com.mao.lookup_method.Chinese">
	  <!-- Spring只要檢測到lookup-method元素,Spring會自動爲該元素的name屬性數指定的方法提供實現體 -->
	  <!-- 方法的返回值就是bean屬性指定的值 -->
	    <property name="dog" ref="gunDog"></property>
	</bean>
	<bean id="gunDog" class="com.mao.lookup_method.GunDog" scope="prototype">
	  <property name="name" value="旺財"></property>
	</bean>

配置文件中我們將gunDog設置成prototype作用股的Bean,並通過普通的依賴注入將prototype Bean注入給singleton Bean

4:測試程序

package com.mao.lookup_method;

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

public class Test 
{
	public static void main(String[]args)
	{
		ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
		Chinese ch1=(Chinese) ctx.getBean("ch");
		Chinese ch2=(Chinese) ctx.getBean("ch");
		System.out.println(ch2==ch2);
		ch1.hunt();
		ch2.hunt();
	}
}

主程序中,我們分兩次獲取了singleton Bean 並判斷是不是同一實例,並分別輸出兩次的hunt()方法,來看看通過singleton Bean訪問prototype Bean是不是也是同一prototype實例

5:輸出結果



從結果可以看出,兩次獲取的singleton Bean是同一實例,但是獲取的prototype Bean實例也是同一實例,但是按照常理來講prototype應該是獲取一次創建一個實例啊,這樣就是去了prototype的作用。

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

二:lookup方法注入

爲了協調作用域不同步的Bean,Spring提供了lookup方法注入,爲了使用lookup方法注入,需要如下兩步

1:將調用者的Bean實現類定義爲抽象類,並定義一個抽象方法來獲取被依賴的Bean

2:在<bean>元素中添加<lookup-method>子元素讓Spring爲調用者Bean的實現類實現指定的抽象方法,

添加<lookup-method>子元素後,<lookup-method>子元素告訴Spring需要實現哪個抽象方法,Spring爲抽象方法提供實現體以後,這個方法就會變成具體類,下面Spring就可以創建該Bean的實例了。

使<lookup-method>元素要制定如下兩個屬性

1:name:告訴Spring需要實現哪個抽象方法

2:bean:指定Spring實現該方法後的返回值


下面是協調作用域不同步Bean的例子,我們將上面的例子稍作修改

1:首先將調用者chinese Bean定義爲抽象類,並定義抽象方法獲取依賴Bean

package com.mao.lookup_method;

public abstract class Chinese implements Person
{
	private Dog dog;
	//定義抽象方法,該方法用於獲取被依賴的Bean
	
	public abstract Dog getDog();
	
	@Override
	public void hunt() 
	{
		// TODO Auto-generated method stub
		System.out.println("我帶着"+getDog()+"出去打獵");
		System.out.println(getDog().run());
	}

}
上面我們將調用者Chinese Bean定義爲抽象類,並定義抽象方法getDog()用於獲取被依賴Bean ,該方法返回的是被依賴Bean的實例,這裏返回的就是gunDog Bean實例,該方法由Spring容器自動調用,然後我們再多次調用hunt()方法從singleton Bean獲取prototype Bean,看看是不是獲取的還是同一prototype Bean實例。

2:被依賴的gunDog Bean 

package com.mao.lookup_method;

public class GunDog implements Dog
{
	private String name;

	public GunDog() 
	{
	
	}

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

	@Override
	public String run() 
	{
		return"我是一個叫"+name+"的獵犬,奔跑迅速》》》》" ;
		
	}

}

3:配置文件 beans.aml

        <bean id="ch" class="com.mao.lookup_method.Chinese">
	  <!-- Spring只要檢測到lookup-method元素,Spring會自動爲該元素的name屬性數指定的方法提供實現體 -->
	  <!-- 方法的返回值就是bean屬性指定的值 -->
	  <lookup-method name="getDog" bean="gunDog"/>
	</bean>
	<bean id="gunDog" class="com.mao.lookup_method.GunDog" scope="prototype">
	  <property name="name" value="旺財"></property>
	</bean>

上面在定義Chinese Bean的時候,添加了<lookup-method>子元素,該元素內有name、bean屬性,就是說Spring應負責實現getDog()方法,該方法的返回值是容器中的gunDog Bean實例。並且我們將gunDog Bean作用域設置成prototype

4:測試函數 Test.java

package com.mao.lookup_method;

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

public class Test 
{
	public static void main(String[]args)
	{
		ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
		Chinese ch1=(Chinese) ctx.getBean("ch");
		Chinese ch2=(Chinese) ctx.getBean("ch");
		System.out.println(ch2==ch2);
		ch1.hunt();
		ch2.hunt();
	}
}

主函數分兩次獲取了singleton Bean,並通過hun()t兩次訪問prototype Bean

5:運行結果


可以看出,兩次訪問的prototype是兩個不同的實例。

使用lookup方法注入後,系統每次調用getDog()方法都會獲得新的gunDog 實例,這就保證當singleton作用域Bean需要prototype  Bean時,直接調用GetDog方法即可獲得全新實例。這也正是lookup方法注入和普通方法注入的區別





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