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

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

方法一:在初始化時保存ApplicationContext對象
方法二:經過Spring提供的工具類獲取ApplicationContext對象
方法三:繼承自抽象類ApplicationObjectSupport
方法四:繼承自抽象類WebApplicationObjectSupport
方法五:實現接口ApplicationContextAwarejava

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

方法一:在初始化時保存ApplicationContext對象spring

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。session

 

方法三:繼承自抽象類ApplicationObjectSupport
說明:抽象類ApplicationObjectSupport提供getApplicationContext()方法,能夠方便的獲取到ApplicationContext。
Spring初始化時,會經過該抽象類的setApplicationContext(ApplicationContext context)方法將ApplicationContext 對象注入。app

 

方法四:繼承自抽象類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的做用範圍

1、 問題描述

 

       在大部分狀況下,容器中的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實例

 



2、 解決方案

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

  • 放棄控制反轉。

           經過實現ApplicationContextAware接口讓bean A可以感知bean 容器,而且在須要的時候經過使用getBean("B")方式向容器請求一個新的bean B實例。

  • Lookup方法注入。

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

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

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

 

3、 實現案例

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規則,因此儘可能採用這種方式。


4、 原理分析(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技術,你們之後遇到的時候能夠搜一下!)

 

5、 新的擴展(註解方式)

    自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.   

 就是說讓方法異步執行。

相關文章
相關標籤/搜索