在整理之前,我們首先應該瞭解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方法注入和普通方法注入的區別