1. 程式人生 > >Spring注入非單例bean以及scope的作用範圍

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

    介面類:

Java程式碼  收藏程式碼
  1. package learn.frame.spring.scope.dropioc;  
  2. public interface Command {  
  3.     public Object execute();  
  4. }  

     實現類:

Java程式碼  收藏程式碼
  1. package learn.frame.spring.scope.dropioc;  
  2. public class AsyncCommand implements Command {  
  3.     @Override  
  4.     public Object execute() {  
  5.         return this;  
  6.     }  
  7. }  

    業務類:

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

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

     配置檔案:beans-dropioc.xml

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

     scope="prototype"

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.     <bean id="commandManager" class="learn.frame.spring.scope.dropioc.CommandManager">  
  10.     </bean>   
  11. </beans>   

    測試類:

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

 Test結果:

Java程式碼  收藏程式碼
  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配置和業務類。

    新的業務:

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

    配置檔案:beans-lookup.xml

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.     <bean id="commandManager" class="learn.frame.spring.scope.lookup.CommandManager">    
  10.             <lookup-method name="createCommand" bean="asyncCommand"/>    
  11.         </bean>  
  12. </beans>   

    變化部分:

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

    測試類:

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

 測試結果:

Java程式碼  收藏程式碼
  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。 Java程式碼  收藏程式碼
  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格式的文件生命中使用,類似於如下程式碼所演示的形式:

Xml程式碼  收藏程式碼
  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,所以,下面三種配置,形式實際上達成的是同樣的效果:

Xml程式碼  收藏程式碼
  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:

Java程式碼  收藏程式碼
  1. DTD:  
  2. <bean id ="mockObject1" class="..." singleton="false" />  
  3. XSD:  
  4. <bean id ="mockObject1" class="..."   scope="prototype" />  

4.3 request ,session和global session

相關推薦

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

本文轉載自:http://www.cnblogs.com/duanxz/archive/2014/06/18/3794075.html 方法一:在初始化時儲存ApplicationContext物件方法二:通過Spring提供的工具類獲取ApplicationContext物件方法三:繼承自抽象類Appli

Spring注入bean以及scope作用範圍

一、 問題描述 在大部分情況下,容器中的bean都是singleton型別的。 如果一個singleton bean要引用另外一個singleton bean,或者一個非singleton bean要引用另外一個非singleton bean時,通常情況下將一個b

spring注入beanscope作用範圍

一、 問題描述 在大部分情況下,容器中的bean都是singleton型別的。 如果一個singleton bean要引用另外一個singleton bean,或者一個非singleton bean要引用另外一個非singleton bean時,通常情況下將一個b

Spring方法注入bean的呼叫

引用 在大部分情況下,容器中的bean都是singleton型別的。如果一個singleton bean要引用另外一個singleton bean,或者一個非singleton bean要引用另外一個非singleton bean時,通常情況下將一個bean定義為另一個be

spring建立beanbean的方式

利用spring建立單例bean,這通常是spring建立bean的預設方式。不過我們可以在建立bean的時候進行顯示宣告,例:在bean的配置檔案中加上scope屬性 1> 建立單例bean <bean id="boss" class="com.zj.Boss

20--Spring例項化bean的準備工作

上一節分析了Spring從快取中獲取單例bean的過程,相對於建立一個全新的單例bean,該過程還是很簡單的,本節接著分析建立單例bean的準備工作。 引 protected <T> T doGetBean(final String name, @N

Spring 原始碼學習 - bean的例項化過程

> 本文作者:geek,一個聰明好學的同事 ## 1. 簡介 開發中我們常用@Commpont,@Service,@Resource等註解或者配置xml去宣告一個類,使其成為spring容器中的bean,以下我將用從原始碼角度看以AnnotationConfigApplicationContext

spring bean和多的使用場景和在bean注入(不看後悔,一看必懂)

為什麼用單例或者多例?何時用? 之所以用單例,是因為沒必要每個請求都新建一個物件,這樣子既浪費CPU又浪費記憶體; 之所以用多例,是為了防止併發問題;即一個請求改變了物件的狀態,此時物件又處理另一個請求,而之前請求對物件狀態的改變導致了物件對另一個請求做了錯誤的處理;  

011. Spring Bean

1、建立Java專案:File -> New -> Java Project 2、引入必要jar包,專案結構如下 3、建立People實體類People.java package com.spring.model; public clas

spring多檔案配置,手工注入,自動注入

一關聯類 1Student.java package study.spring; public class Student{     private String id;    private String name;    private Address address; 

Spring IOC 容器源碼分析 - 創建 bean 的過程

event version trac 方法 del lB ctu prepare 先來 1. 簡介 在上一篇文章中,我比較詳細的分析了獲取 bean 的方法,也就是getBean(String)的實現邏輯。對於已實例化好的單例 bean,getBean(String) 方法

Spring源碼分析(十三)緩存中獲取bean

ould for 目的 存儲 不同 單例 color 正在 span 摘要:本文結合《Spring源碼深度解析》來分析Spring 5.0.6版本的源代碼。若有描述錯誤之處,歡迎指正。 介紹過FactoryBean的用法後,我們就可以了解bean加載的過程了。前面已

Spring beanbean的執行緒安全

Bean的作用域    Spring 3中為Bean定義了5中作用域,分別為singleton(單例)、prototype(原型)、request、session和global session,5種作用域說明如下: singleton:單例模式,Spring I

spring深入學習(十五) IOC 之從快取中獲取 bean

從這篇部落格開始我們開始載入 bean 的第一個步驟,從快取中獲取 bean,程式碼片段如下: Object sharedInstance = getSingleton(beanName); if (sharedInstance != null &&

Spring IOC(一)+容器關閉了,getBean獲取的物件(小老弟)你咋還在蹦躂? day--07

Spring IOC(一)單例、非單例+容器關閉了,getBean獲取的物件(小老弟)你咋還在蹦躂? day–07 一路綠燈學過去的就不記了,只記錄重點和實驗過程,另外內容順序排列可能引起不適,但是是根我碰到問題順序走的,,,在這向有一丁點可能看到這篇文章的您抱歉。 一、.Sprin

Spring原始碼閱讀】 preInstantiateSingletons方法分析,Bean獲取/例項化流程

在初始化ClassPathXmlApplicatonContext過程中,核心初始化邏輯在AbstractApplicationContext的refresh函式中: public void refresh() throws BeansException, IllegalStateE

Spring建立bean

Spring建立bean是有一個Scope。預設是單例。 如何證明Spring建立的Bean就是單例?Spring單例有什麼意義在? Spring建立的bean是否是單例很好判斷: ApplicationContext context = new Cl

spring中的——scope

今天研究了一下scope的作用域。預設是單例模式,即scope="singleton"。另外scope還有prototype、request、session、global session作用域。scope="prototype"多例。再配置bean的作用域時,它的標頭檔案形

spring ioc中例項化bean以及依賴注入bean和基本型別屬性簡單實現方案

Spring兩個重要特性IOC和AOP,本文中簡單實現bean的建立以及屬性依賴注入。 實現步驟如下: 1、通過dom4j解析bean.xml檔案,將bean物件以及依賴屬性裝入List。 2、遍歷list,通過反射機制例項化物件。 3、遍歷list,通過反射呼叫bean裡

Spring SingletonBeanRegistry Bean 註冊管理

會銷 efi 對象 實例化 如果 tro nullable 還在 ntb Spring SingletonBeanRegistry 單例 Bean 註冊管理 在 Spring 中 Bean 有單例和多例之分,只有單例的 bean 才會被 BeanFactory 管理,這個