1. 程式人生 > >普通Java類獲取spring 容器的bean的5種方法 Spring注入非單例bean以及scope的作用範圍

普通Java類獲取spring 容器的bean的5種方法 Spring注入非單例bean以及scope的作用範圍

本文轉載自:http://www.cnblogs.com/duanxz/archive/2014/06/18/3794075.html

方法一:在初始化時儲存ApplicationContext物件
方法二:通過Spring提供的工具類獲取ApplicationContext物件
方法三:繼承自抽象類ApplicationObjectSupport
方法四:繼承自抽象類WebApplicationObjectSupport
方法五:實現介面ApplicationContextAware

常用的5種獲取spring 中bean的方式總結:

方法一:在初始化時儲存ApplicationContext物件

ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml");
ac.getBean("beanId");

說明:這種方式適用於採用Spring框架的獨立應用程式,需要程式通過配置檔案手工初始化Spring的情況。

 

方法二:通過Spring提供的工具類獲取ApplicationContext物件

import org.springframework.web.context.support.WebApplicationContextUtils;
ApplicationContext ac1 = WebApplicationContextUtils.getRequiredWebApplicationContext(ServletContext sc);
ApplicationContext ac2 = WebApplicationContextUtils.getWebApplicationContext(ServletContext sc);
ac1.getBean("beanId");
ac2.getBean("beanId");

說明:
這種方式適合於採用Spring框架的B/S系統,通過ServletContext物件獲取ApplicationContext物件,然後在通過它獲取需要的類例項。

上面兩個工具方式的區別是,前者在獲取失敗時丟擲異常,後者返回null。

 

方法三:繼承自抽象類ApplicationObjectSupport
說明:抽象類ApplicationObjectSupport提供getApplicationContext()方法,可以方便的獲取到ApplicationContext。
Spring初始化時,會通過該抽象類的setApplicationContext(ApplicationContext context)方法將ApplicationContext 物件注入。

 

方法四:繼承自抽象類WebApplicationObjectSupport
說明:類似上面方法,呼叫getWebApplicationContext()獲取WebApplicationContext

 

方法五:實現介面ApplicationContextAware
說明:實現該介面的setApplicationContext(ApplicationContext context)方法,並儲存ApplicationContext 物件。
Spring初始化時,會通過該方法將ApplicationContext物件注入。

 

雖然,spring提供了後三種方法可以實現在普通的類中繼承或實現相應的類或介面來獲取spring 的ApplicationContext物件,但是在使用是一定要注意實現了這些類或介面的普通java類一定要在Spring 的配置檔案application-context.xml檔案中進行配置。否則獲取的ApplicationContext物件將為null。

 

如下是我實現了ApplicationContextAware介面的例子

複製程式碼
package com.dxz.spring.ioc;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class SpringConfigTool implements ApplicationContextAware {// extends
                                                                    // ApplicationObjectSupport{

    private static ApplicationContext context = null;
    private static SpringConfigTool stools = null;

    public synchronized static SpringConfigTool init() {
        if (stools == null) {
            stools = new SpringConfigTool();
        }
        return stools;
    }

    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        context = applicationContext;
    }

    public synchronized static Object getBean(String beanName) {
        return context.getBean(beanName);
    }

}
複製程式碼

其次在applicationContext.xml檔案進行配置:

<bean id="SpringConfigTool" class="com.dxz.spring.ioc.SpringConfigTool"/>

最後提供一種不依賴於servlet,不需要注入的方式
注意一點,在伺服器啟動時,Spring容器初始化時,不能通過以下方法獲取Spring 容器,如需細節可以觀看原始碼org.springframework.web.context.ContextLoader

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->1 import org.springframework.web.context.ContextLoader; 2 import org.springframework.web.context.WebApplicationContext; 3 4 WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext(); 5 wac.getBean(beanID);

Spring注入非單例bean以及scope的作用範圍

一、 問題描述

 

       在大部分情況下,容器中的bean都是singleton型別的。

 

       如果一個singleton bean要引用另外一個singleton bean,或者一個非singleton bean要引用另外一個非singleton bean時,通常情況下將一個bean定義為另一個bean的property值就可以了。不過對於具有不同生命週期的bean來說這樣做就會有問題了,比如在呼叫一個singleton型別bean A的某個方法時,需要引用另一個非singleton(prototype)型別的bean B,對於bean A來說,容器只會建立一次,這樣就沒法在需要的時候每次讓容器為bean A提供一個新的的bean B例項

 



二、 解決方案

       對於上面的問題Spring提供了三種解決方案:

  • 放棄控制反轉。

           通過實現ApplicationContextAware介面讓bean A能夠感知bean 容器,並且在需要的時候通過使用getBean("B")方式向容器請求一個新的bean B例項。

  • Lookup方法注入。

            Lookup方法注入利用了容器的覆蓋受容器管理的bean方法的能力,從而返回指定名字的bean例項。

  • 自定義方法的替代方案。

            該注入能使用bean的另一個方法實現去替換自定義的方法。

 

三、 實現案例

3.1 放棄IOC

 

    介面類:

  1. package learn.frame.spring.scope.dropioc;  
  2.   
  3. public interface Command {  
  4.     public Object execute();  
  5. }  

 

     實現類:

  1. package learn.frame.spring.scope.dropioc;  
  2.   
  3. public class AsyncCommand implements Command {  
  4.   
  5.     @Override  
  6.     public Object execute() {  
  7.         return this;  
  8.     }  
  9.   
  10. }  

 

    業務類:

     ApplicationContextAware和BeanFactoryAware差不多,用法也差不多,實現了ApplicationContextAware介面的物件會擁有        一個ApplicationContext的引用,這樣我們就可以已程式設計的方式操作ApplicationContext。看下面的例子。

  1. public class CommandManager implements ApplicationContextAware {  
  2.   
  3.     //用於儲存ApplicationContext的引用,set方式注入     
  4.     private ApplicationContext applicationContext;  
  5.   
  6.     //模擬業務處理的方法     
  7.     public Object process() {  
  8.         Command command = createCommand();  
  9.         return command.execute();  
  10.     }  
  11.   
  12.     //獲取一個命令     
  13.     private Command createCommand() {  
  14.         return (Command) this.applicationContext.getBean("asyncCommand"); //     
  15.     }  
  16.   
  17.     @Override  
  18.     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {  
  19.         this.applicationContext = applicationContext;//獲得該ApplicationContext引用     
  20.     }  
  21. }  

 

     配置檔案:beans-dropioc.xml

    單例Bean commandManager的process()方法需要引用一個prototype(非單例)的bean,所以在呼叫process的時候先通過            createCommand方法從容器中取得一個Command,然後在執行業務計算。

     scope="prototype"

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans     
  5.         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  6.     <!-- 通過scope="prototype"界定該bean是多例的 -->  
  7.     <bean id="asyncCommand" class="learn.frame.spring.scope.dropioc.AsyncCommand"  
  8.         scope="prototype"></bean>  
  9.       
  10.     <bean id="commandManager" class="learn.frame.spring.scope.dropioc.CommandManager">  
  11.     </bean>   
  12.       
  13. </beans>   

    測試類:

  1. package org.shupeng.learn.frame.spring.scope;  
  2.   
  3. import java.util.ArrayList;  
  4.   
  5. import org.junit.Before;  
  6. import org.junit.Test;  
  7. import learn.frame.spring.scope.dropioc.CommandManager;  
  8. import org.springframework.context.ApplicationContext;  
  9. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  10.   
  11.   
  12. public class TestCommandManagerDropIOC {  
  13.     private ApplicationContext context;  
  14.   
  15.     @Before  
  16.     public void setUp() throws Exception {  
  17.         context = new ClassPathXmlApplicationContext("beans-dropioc.xml");  
  18.     }  
  19.   
  20.     @Test  
  21.     public void testProcess() {  
  22.         CommandManager manager = (CommandManager) context.getBean("commandManager",  
  23.                 CommandManager.class);  
  24.         System.out.println("第一執行process,Command的地址是:" + manager.process());  
  25.         System.out.println("第二執行process,Command的地址是:" + manager.process());  
  26.     }  
  27. }  

 

 Test結果:

  1. 第一執行process,Command的地址是:learn.frame.spring.scope.dropioc.AsyncCommand@187c55c  
  2. 第二執行process,Command的地址是:learn.frame.spring.scope.dropioc.AsyncCommand@ae3364  

     通過控制檯輸出看到兩次的輸出借中的Command的地址是不一樣的,因為我們為asyncCommand配置了scope="prototype"屬性,這種方式就是使得每次從容器中取得的bean例項都不一樣。

      業務程式碼和Spring Framework產生了耦合。

 

3.2 Look方法注入

    這種方式Spring已經為我們做了很大一部分工作,要做的就是bean配置和業務類。

 

    新的業務:

  1. package learn.frame.spring.scope.lookup;  
  2.   
  3. import learn.frame.spring.scope.dropioc.Command;  
  4.   
  5. public abstract class CommandManager {  
  6.   
  7.     //模擬業務處理的方法     
  8.     public Object process() {  
  9.         Command command = createCommand();  
  10.         return command.execute();  
  11.     }  
  12.   
  13.     //獲取一個命令     
  14.     protected abstract Command createCommand();  
  15.   
  16. }  

 

    配置檔案:beans-lookup.xml

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans     
  5.         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  6.     <!-- 通過scope="prototype"界定該bean是多例的 -->  
  7.     <bean id="asyncCommand" class="learn.frame.spring.scope.dropioc.AsyncCommand"  
  8.         scope="prototype"></bean>  
  9.           
  10.     <bean id="commandManager" class="learn.frame.spring.scope.lookup.CommandManager">    
  11.             <lookup-method name="createCommand" bean="asyncCommand"/>    
  12.         </bean>  
  13. </beans>   

 

    變化部分:

  • 修改CommandManager類為abstract的,修改createCommand方法也為abstract的。
  • 去掉ApplicationContextAware的實現及相關set方法和applicationContext變數定義
  • 修改bean配置檔案,在commandManager Bean中增加<lookup-method name="createCommand" bean="asyncCommand"/>。

    測試類:

  1. package learn.frame.spring.scope;  
  2.   
  3. import org.junit.Before;  
  4. import org.junit.Test;  
  5. import learn.frame.spring.scope.lookup.CommandManager;  
  6. import org.springframework.context.ApplicationContext;  
  7. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  8.   
  9. public class TestCommandManagerLookup {  
  10.     private ApplicationContext context;  
  11.   
  12.     @Before  
  13.     public void setUp() throws Exception {  
  14.         context = new ClassPathXmlApplicationContext("beans-lookup.xml");  
  15.     }  
  16.   
  17.     @Test  
  18.     public void testProcess() {  
  19.         CommandManager manager = (CommandManager) context.getBean("commandManager",  
  20.                 CommandManager.class);  
  21.         System.out.println("第一執行process,Command的地址是:" + manager.process());  
  22.         System.out.println("第二執行process,Command的地址是:" + manager.process());  
  23.     }  
  24. }  

 

 測試結果:

  1. 第一執行process,Command的地址是:learn.frame.spring.scope.dropioc.AsyncCommand@5bb966  
  2. 第二執行process,Command的地址是:learn.frame.spring.scope.dropioc.AsyncCommand@1e903d5  

 控制檯打印出的兩個Command的地址不一樣,說明實現了。

 

<lookup-method>標籤中的name屬性就是commandManager Bean的獲取Command例項(AsyncCommand)的方法,也就createCommand方法,bean屬性是要返回哪種型別的Command的,這裡是AsyncCommand。  
  1. <public|protected> [abstract] <return-type> theMethodName(no-arguments)  
  •       被注入方法不一定是抽象的,如果被注入方法是抽象的,動態生成的子類(這裡就是動態生成的CommandManager的子類)會實現該方法。否則,動態生成的子類會覆蓋類裡的具體方法。
  • 為了讓這個動態子類得以正常工作,需要把CGLIB的jar檔案放在classpath裡,這就是我們引用cglib包的原因。
  • Spring容器要子類化的類(CommandManager)不能是final的,要覆蓋的方法(createCommand)也不能是final的。

Lookup方法注入乾淨整潔,易於擴充套件,更符合Ioc規則,所以儘量採用這種方式。


四、 原理分析(bean的scope屬性範圍)

 

       scope用來宣告IOC容器中的物件應該處的限定場景或者說該物件的存活空間,即在IOC容器在物件進入相應的scope之前,生成並裝配這些物件,在該物件不再處於這些scope的限定之後,容器通常會銷燬這些物件。

 

       Spring容器最初提供了兩種bean的scope型別:singleton和prototype,但釋出2.0之後,又引入了另外三種scope型別,即request,session和global session型別。不過這三種類型有所限制,只能在web應用中使用,也就是說,只有在支援web應用的ApplicationContext中使用這三個scope才是合理的。

       可以使用bean的singleton或scope屬性來指定相應物件的scope,其中,scope屬性只能在XSD格式的文件生命中使用,類似於如下程式碼所演示的形式:

 

  1. DTD:  
  2. <bean id ="mockObject1" class="..." singleton="false" />  
  3. XSD:  
  4. <bean id ="mockObject1" class="..."   scope="prototype" />  

       注意:這裡的singleton和設計模式裡面的單例模式不一樣,標記為singleton的bean是由容器來保證這種型別的bean在同一個容器內只存在一個共享例項,而單例模式則是保證在同一個Classloader中只存在一個這種型別的例項。

 

4.1. singleton

      singleton型別的bean定義,在一個容器中只存在一個例項,所有對該型別bean的依賴都引用這一單一例項。singleton型別的bean定義,從容器啟動,到他第一次被請求而例項化開始,只要容器不銷燬或退出,該型別的bean的單一例項就會一直存活。

       通常情況下,如果你不指定bean的scope,singleton便是容器預設的scope,所以,下面三種配置,形式實際上達成的是同樣的效果:

  1. DTD or XSD:  
  2. <bean id ="mockObject1" class="..." />  
  3. DTD:  
  4. <bean id ="mockObject1" class="..." singleton="true" />  
  5. XSD:  
  6. <bean id ="mockObject1" class="..."   scope="singleton" />  

 

4.2 prototype

       scope為prototype的bean,容器在接受到該型別的物件的請求的時候,會每次都重新生成一個新的物件給請求方。

       雖然這種型別的物件的例項化以及屬性設定等工作都是由容器負責的,但是隻要準備完畢,並且物件例項返回給請求方之後,容器就不在擁有當前物件的引用,請求方需要自己負責當前物件後繼生命週期的管理工作,包括該物件的銷燬。也就是說,容器每次返回請求方該物件的一個新的例項之後,就由這個物件“自生自滅”了。

    可以用以下方式定義prototype型別的bean:

  1. DTD:  
  2. <bean id ="mockObject1" class="..." singleton="false" />  
  3. XSD:  
  4. <bean id ="mockObject1" class="..."   scope="prototype" />  

 

4.3 request ,session和global session

      這三個型別是spring2.0之後新增的,他們不像singleton和prototype那麼通用,因為他們只適用於web程式,通常是和XmlWebApplicationContext共同使用。

 

     request:

 

  1. <bean id ="requestPrecessor" class="...RequestPrecessor"   scope="request" />  

       Spring容器,即XmlWebApplicationContext 會為每個HTTP請求建立一個全新的RequestPrecessor物件,當請求結束後,該物件的生命週期即告結束。當同時有10個HTTP請求進來的時候,容器會分別針對這10個請求建立10個全新的RequestPrecessor例項,且他們相互之間互不干擾,從不是很嚴格的意義上說,request可以看做prototype的一種特例,除了場景更加具體之外,語意上差不多。

 

 

        session

       對於web應用來說,放到session中最普遍的就是使用者的登入資訊,對於這種放到session中的資訊,我們我們可以使用如下形式的制定scope為session:

 

  1. <bean id ="userPreferences" class="...UserPreferences"   scope="session" />  

        Spring容器會為每個獨立的session建立屬於自己的全新的UserPreferences例項,他比request scope的bean會存活更長的時間,其他的方面真是沒什麼區別。

 

     global session:

 

  1. <bean id ="userPreferences" class="...UserPreferences"   scope="globalsession" />  

      global session只有應用在基於porlet的web應用程式中才有意義,他對映到porlet的global範圍的session,如果普通的servlet的web 應用中使用了這個scope,容器會把它作為普通的session的scope對待。

(我只是聽說過porlet這個詞,好像是和servlet類似的一種java web技術,大家以後遇到的時候可以搜一下!)

 

五、 新的擴充套件(註解方式)

    自Spring3.x開始,增加了@Async這樣一個註解,Spring 文件裡是這樣說的:

 

  1.  The @Async annotation can be provided on a method so that invocation of that method will occur asynchronously. </br>  
  2. In other words, the caller will return immediately upon invocation and the actual execution of the method will </br>  
  3. occur in a task that has been submitted to a Spring TaskExecutor.   

 就是說讓方法非同步執行。

一、 問題描述

 

       在大部分情況下,容器中的bean都是singleton型別的。

 

       如果一個singleton bean要引用另外一個singleton bean,或者一個非singleton bean要引用另外一個非singleton bean時,通常情況下將一個bean定義為另一個bean的property值就可以了。不過對於具有不同生命週期的bean來說這樣做就會有問題了,比如在呼叫一個singleton型別bean A的某個方法時,需要引用另一個非singleton(prototype)型別的bean B,對於bean A來說,容器只會建立一次,這樣就沒法在需要的時候每次讓容器為bean A提供一個新的的bean B例項

 



二、 解決方案

       對於上面的問題Spring提供了三種解決方案:

  • 放棄控制反轉。

           通過實現ApplicationContextAware介面讓bean A能夠感知bean 容器,並且在需要的時候通過使用getBean("B")方式向容器請求一個新的bean B例項。

  • Lookup方法注入。

            Lookup方法注入利用了容器的覆蓋受容器管理的bean方法的能力,從而返回指定名字的bean例項。

  • 自定義方法的替代方案。

            該注入能使用bean的另一個方法實現去替換自定義的方法。

 

三、 實現案例

3.1 放棄IOC

 

    介面類:

  1. package learn.frame.spring.scope.dropioc;  
  2.   
  3. public interface Command {  
  4.     public Object execute();  
  5. }  

 

     實現類:

  1. package learn.frame.spring.scope.dropioc;  
  2.   
  3. public class AsyncCommand implements Command {  
  4.   
  5.     @Override  
  6.     public Object execute() {  
  7.         return this;  
  8.     }  
  9.   
  10. }  

 

    業務類:

     ApplicationContextAware和BeanFactoryAware差不多,用法也差不多,實現了ApplicationContextAware介面的物件會擁有        一個ApplicationContext的引用,這樣我們就可以已程式設計的方式操作ApplicationContext。看下面的例子。

  1. public class CommandManager implements ApplicationContextAware {  
  2.   
  3.     //用於儲存ApplicationContext的引用,set方式注入     
  4.     private ApplicationContext applicationContext;  
  5.   
  6.     //模擬業務處理的方法     
  7.     public Object process() {  
  8.         Command command = createCommand();  
  9.         return command.execute();  
  10.     }  
  11.   
  12.     //獲取一個命令     
  13.     private Command createCommand() {  
  14.         return (Command) this.applicationContext.getBean("asyncCommand"); //     
  15.     }  
  16.   
  17.     @Override  
  18.     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {  
  19.         this.applicationContext = applicationContext;//獲得該ApplicationContext引用     
  20.     }  
  21. }  

 

     配置檔案:beans-dropioc.xml

    單例Bean commandManager的process()方法需要引用一個prototype(非單例)的bean,所以在呼叫process的時候先通過            createCommand方法從容器中取得一個Command,然後在執行業務計算。

     scope="prototype"

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans     
  5.         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  6.     <!-- 通過scope="prototype"界定該bean是多例的 -->  
  7.     <bean id="asyncCommand" class="learn.frame.spring.scope.dropioc.AsyncCommand"  
  8.         scope="prototype"></bean>  
  9.       
  10.     <bean id="commandManager" class="learn.frame.spring.scope.dropioc.CommandManager">  
  11.     </bean>   
  12.       
  13. </beans>   

    測試類:

  1. package org.shupeng.learn.frame.spring.scope;  
  2.   
  3. import java.util.ArrayList;  
  4.   
  5. import org.junit.Before;  
  6. import org.junit.Test;  
  7. import learn.frame.spring.scope.dropioc.CommandManager;  
  8. import org.springframework.context.ApplicationContext;  
  9. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  10.   
  11.   
  12. public class TestCommandManagerDropIOC {  
  13.     private ApplicationContext context;  
  14.   
  15.     @Before  
  16.     public void setUp() throws Exception {  
  17.         context = new ClassPathXmlApplicationContext("beans-dropioc.xml");  
  18.     }  
  19.   
  20.     @Test  
  21.     public void testProcess() {  
  22.         CommandManager manager = (CommandManager) context.getBean("commandManager",  
  23.                 CommandManager.class);  
  24.         System.out.println("第一執行process,Command的地址是:" + manager.process());  
  25.         System.out.println("第二執行process,Command的地址是:" + manager.process());  
  26.     }  
  27. }  

 

 Test結果:

  1. 第一執行process,Command的地址是:learn.frame.spring.scope.dropioc.AsyncCommand@187c55c  
  2. 第二執行process,Command的地址是:learn.frame.spring.scope.dropioc.AsyncCommand@ae3364  

     通過控制檯輸出看到兩次的輸出借中的Command的地址是不一樣的,因為我們為asyncCommand配置了scope="prototype"屬性,這種方式就是使得每次從容器中取得的bean例項都不一樣。

      業務程式碼和Spring Framework產生了耦合。

 

3.2 Look方法注入

    這種方式Spring已經為我們做了很大一部分工作,要做的就是bean配置和業務類。

 

    新的業務:

  1. package learn.frame.spring.scope.lookup;  
  2.   
  3. import learn.frame.spring.scope.dropioc.Command;  
  4.   
  5. public abstract class CommandManager {  
  6.   
  7.     //模擬業務處理的方法     
  8.     public Object process() {  
  9.         Command command = createCommand();  
  10.         return command.execute();  
  11.     }  
  12.   
  13.     //獲取一個命令     
  14.     protected abstract Command createCommand();  
  15.   
  16. }  

 

    配置檔案:beans-lookup.xml

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans     
  5.         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  6.     <!-- 通過scope="prototype"界定該bean是多例的 -->  
  7.     <bean id="asyncCommand" class="learn.frame.spring.scope.dropioc.AsyncCommand"  
  8.         scope="prototype"></bean>  
  9.           
  10.     <bean id="commandManager" class="learn.frame.spring.scope.lookup.CommandManager">    
  11.             <lookup-method name="createCommand" bean="asyncCommand"/>    
  12.         </bean>  
  13. </beans>   

 

    變化部分:

  • 修改CommandManager類為abstract的,修改createCommand方法也為abstract的。
  • 去掉ApplicationContextAware的實現及相關set方法和applicationContext變數定義
  • 修改bean配置檔案,在commandManager Bean中增加<lookup-method name="createCommand" bean="asyncCommand"/>。

    測試類:

  1. package learn.frame.spring.scope;  
  2.   
  3. import org.junit.Before;  
  4. import org.junit.Test;  
  5. import learn.frame.spring.scope.lookup.CommandManager;  
  6. import org.springframework.context.ApplicationContext;  
  7. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  8.   
  9. public class TestCommandManagerLookup {  
  10.     private ApplicationContext context;  
  11.   
  12.     @Before  
  13.     public void setUp() throws Exception {  
  14.         context = new ClassPathXmlApplicationContext("beans-lookup.xml");  
  15.     }  
  16.   
  17.     @Test  
  18.     public void testProcess() {  
  19.         CommandManager manager = (CommandManager) context.getBean("commandManager",  
  20.                 CommandManager.class);  
  21.         System.out.println("第一執行process,Command的地址是:" + manager.process());  
  22.         System.out.println("第二執行process,Command的地址是:" + manager.process());  
  23.     }  
  24. }  

 

 測試結果:

  1. 第一執行process,Command的地址是:learn.frame.spring.scope.dropioc.AsyncCommand@5bb966  
  2. 第二執行process,Command的地址是:learn.frame.spring.scope.dropioc.AsyncCommand@1e903d5  

 控制檯打印出的兩個Command的地址不一樣,說明實現了。

 

<lookup-method>標籤中的name屬性就是commandManager Bean的獲取Command例項(AsyncCommand)的方法,也就createCommand方法,bean屬性是要返回哪種型別的Command的,這裡是AsyncCommand。  
  1. <public|protected> [abstract] <return-type> theMethodName(no-arguments)  
  •       被注入方法不一定是抽象的,如果被注入方法是抽象的,動態生成的子類(這裡就是動態生成的CommandManager的子類)會實現該方法。否則,動態生成的子類會覆蓋類裡的具體方法。
  • 為了讓這個動態子類得以正常工作,需要把CGLIB的jar檔案放在classpath裡,這就是我們引用cglib包的原因。
  • Spring容器要子類化的類(CommandManager)不能是final的,要覆蓋的方法(createCommand)也不能是final的。

Lookup方法注入乾淨整潔,易於擴充套件,更符合Ioc規則,所以儘量採用這種方式。


四、 原理分析(bean的scope屬性範圍)

 

       scope用來宣告IOC容器中的物件應該處的限定場景或者說該物件的存活空間,即在IOC容器在物件進入相應的scope之前,生成並裝配這些物件,在該物件不再處於這些scope的限定之後,容器通常會銷燬這些物件。

 

       Spring容器最初提供了兩種bean的scope型別:singleton和prototype,但釋出2.0之後,又引入了另外三種scope型別,即request,session和global session型別。不過這三種類型有所限制,只能在web應用中使用,也就是說,只有在支援web應用的ApplicationContext中使用這三個scope才是合理的。

       可以使用bean的singleton或scope屬性來指定相應物件的scope,其中,scope屬性只能在XSD格式的文件生命中使用,類似於如下程式碼所演示的形式:

 

  1. DTD:  
  2. <bean id ="mockObject1" class="..." singleton="false" />  
  3. XSD:  
  4. <bean id ="mockObject1" class="..."   scope="prototype" />  

       注意:這裡的singleton和設計模式裡面的單例模式不一樣,標記為singleton的bean是由容器來保證這種型別的bean在同一個容器內只存在一個共享例項,而單例模式則是保證在同一個Classloader中只存在一個這種型別的例項。

 

4.1. singleton

      singleton型別的bean定義,在一個容器中只存在一個例項,所有對該型別bean的依賴都引用這一單一例項。singleton型別的bean定義,從容器啟動,到他第一次被請求而例項化開始,只要容器不銷燬或退出,該型別的bean的單一例項就會一直存活。

       通常情況下,如果你不指定bean的scope,singleton便是容器預設的scope,所以,下面三種配置,形式實際上達成的是同樣的效果:

  1. DTD or XSD:  
  2. <bean id ="mockObject1" class="..." />  
  3. DTD:  
  4. <bean id ="mockObject1" class="..." singleton="true" />  
  5. XSD:  
  6. <bean id ="mockObject1" class="..."   scope="singleton" />  

 

4.2 prototype

       scope為prototype的bean,容器在接受到該型別的物件的請求的時候,會每次都重新生成一個新的物件給請求方。

       雖然這種型別的物件的例項化以及屬性設定等工作都是由容器負責的,但是隻要準備完畢,並且物件例項返回給請求方之後,容器就不在擁有當前物件的引用,請求方需要自己負責當前物件後繼生命週期的管理工作,包括該物件的銷燬。也就是說,容器每次返回請求方該物件的一個新的例項之後,就由這個物件“自生自滅”了。

    可以用以下方