1. 程式人生 > >Spring方法注入非單例bean的呼叫

Spring方法注入非單例bean的呼叫

引用
在大部分情況下,容器中的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的另一個方法實現去替換自定義的方法。




這裡只說前兩種方案的實現,第三種方案因為不常用,略過不提,有興趣的可以瞭解一下。


一:實現環境
Eclipse3.4
JDK1.5
Spring3.0.3
Junit 4測試框架
依賴jar有log4j-1.2.16.jar,commons-logging-api-1.1.1.jar,cglib-nodep-2.2.jar。


二:通過實現ApplicationContextAware介面以程式設計的方式實現
ApplicationContextAware和BeanFactoryAware差不多,用法也差不多,實現了ApplicationContextAware介面的物件會擁有一個ApplicationContext的引用,這樣我們就可以已程式設計的方式操作ApplicationContext。看下面的例子。
Java程式碼  
package com.flysnow.injection;   
  
import org.springframework.beans.BeansException;   
import org.springframework.context.ApplicationContext;   
import org.springframework.context.ApplicationContextAware;   
  
import com.flysnow.injection.command.Command;   
  
  
public class CommandManager implements ApplicationContextAware {   
    //用於儲存ApplicationContext的引用,set方式注入   
    private ApplicationContext applicationContext;   
    //模擬業務處理的方法   
    public Object process(){   
        Command command=createCommand();   
        return command.execute();   
    }   
    //獲取一個命令   
    private Command createCommand() {   
        return (Command) this.applicationContext.getBean("asyncCommand"); //   
    }   
  
    public void setApplicationContext(ApplicationContext applicationContext)   
            throws BeansException {   
        this.applicationContext=applicationContext;//獲得該ApplicationContext引用   
    }   
  
}  


下面定義Command介面和其實現類AsyncCommand。
Java程式碼  
package com.flysnow.injection.command;   
  
  
public interface Command {   
      
    public Object execute();   
}  


Java程式碼  
package com.flysnow.injection.command;   
  
  
public class AsyncCommand implements Command {   
  
      
    public Object execute() {   
        //返回自身例項,是為了測試的時候好看出每次返回的不是同一個例項   
        return this;   
    }   
  
}  


Bean配置檔案如下:
Xml程式碼  
<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans   
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
        <!-- 通過scope="prototype"界定該bean是多例的 -->  
        <bean id="asyncCommand" class="com.flysnow.injection.command.AsyncCommand" scope="prototype"></bean>  
        <bean id="commandManager" class="com.flysnow.injection.CommandManager">  
        </bean>  
</beans>  


以上主要是單例Bean commandManager的process()方法需要引用一個prototype(非單例)的bean,所以在呼叫process的時候先通過createCommand方法從容器中取得一個Command,然後在執行業務計算,程式碼中有註釋,很簡單。
測試類如下:
Java程式碼  
package com.flysnow.injection;   
  
import org.junit.Before;   
import org.junit.Test;   
import org.springframework.context.ApplicationContext;   
import org.springframework.context.support.ClassPathXmlApplicationContext;   
  
import com.flysnow.injection.CommandManager;   
  
public class TestCommandManager {   
    private ApplicationContext context;   
    @Before  
    public void setUp() throws Exception {   
        context=new ClassPathXmlApplicationContext("beans.xml");   
    }   
  
    @Test  
    public void testProcess() {   
        CommandManager manager=context.getBean("commandManager", CommandManager.class);   
        System.out.println("第一執行process,Command的地址是:"+manager.process());   
        System.out.println("第二執行process,Command的地址是:"+manager.process());   
    }   
  
}  




可以通過控制檯輸出看到兩次的輸出借中的Command的地址是不一樣的,因為我們為asyncCommand配置了scope="prototype"屬性,這種方式就是使得每次從容器中取得的bean例項都不一樣。通過這樣方式我們實現了單例bean(commandManager)中的方法(process方法)引用非單例的bean(asyncCommand)。雖然我們實現了,但是這不是一種好的方法,因為我們的業務程式碼和Spring Framework產生了耦合。下面介紹Spring提供的另外一種乾淨的實現方式,就是Lookup方法注入。


三:通過Lookup方法注入來實現
使用這種方式很簡單,因為Spring已經為我們做了很大一部分工作,我們要做的就是bean配置和業務類。
首先修改CommandManager類為abstract的,修改createCommand方法也為abstract的。
去掉ApplicationContextAware的實現及相關set方法和applicationContext變數定義
修改bean配置檔案,在commandManager Bean中增加<lookup-method name="createCommand" bean="asyncCommand"/>。
其他保持不變


修改後的CommandManager和bean配置檔案如下:
Java程式碼  
public abstract class CommandManager {   
    //模擬業務處理的方法   
    public Object process(){   
        Command command=createCommand();   
        return command.execute();   
    }   
    //獲取一個命令   
    protected abstract Command createCommand();   
}  


Xml程式碼  
<bean id="commandManager" class="com.flysnow.injection.CommandManager">  
            <lookup-method name="createCommand" bean="asyncCommand"/>  
        </bean>  


執行測試,控制檯打印出的兩個Command的地址不一樣,說明我們實現了。
<lookup-method>標籤中的name屬性就是commandManager Bean的獲取Command例項(AsyncCommand)的方法,也就createCommand方法,bean屬性是要返回哪種型別的Command的,這裡是AsyncCommand。
這裡的createCommand方法就成為被注入方法,他的定義形式必須為:
Java程式碼  
<public|protected> [abstract] <return-type> theMethodName(no-arguments);  


被注入方法不一定是抽象的,如果被注入方法是抽象的,動態生成的子類(這裡就是動態生成的CommandManager的子類)會實現該方法。否則,動態生成的子類會覆蓋類裡的具體方法。為了讓這個動態子類得以正常工作,需要把CGLIB的jar檔案放在classpath裡,這就是我們引用cglib包的原因。還有,Spring容器要子類化的類(CommandManager)不能是final的,要覆蓋的方法(createCommand)也不能是final的。


四:小結

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

jsp訪問spring物件(擴充套件)

<%@page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@page import="org.springframework.context.ApplicationContext"%>
<%@page import="org.springframework.web.context.support.WebApplicationContextUtils"%>
<%@page import="ccp.suddenattack.service.news.*"
%> <%@page import="com.netstar8.util.page.*"%> <%@page import="net.sf.json.*"%> <% ApplicationContext context=WebApplicationContextUtils.getWebApplicationContext(application); NewsService newsService = (NewsService)context.getBean("newsService"); PaginationSupport ps = newsService.findAllNews(10,0);
//out.println(ps.getTotalCount()); JSONObject jsonObject = JSONObject.fromObject(ps); String jsonStr = "json503="+jsonObject.toString(); out.println(jsonStr); %>

相關推薦

Spring方法注入bean呼叫

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

普通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注入bean及scope的作用範圍

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

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

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

spring建立beanbean的方式

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

死磕Spring之IoC篇 - Bean 的迴圈依賴處理

> 該系列文章是本人在學習 Spring 的過程中總結下來的,裡面涉及到相關原始碼,可能對讀者不太友好,請結合我的原始碼註釋 [Spring 原始碼分析 GitHub 地址](https://github.com/liu844869663/spring-framework) 進行閱讀 > > Spring 版

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

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

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

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

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

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

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

Spring(06)——注入注入ApplicationContext

6 單例注入多例之注入ApplicationContextSpring bean容器中的bean有單例bean,也有多例bean。當我們需要給一個單例beanA注入一個單例beanB,或者給一個多例be

Spring建立bean

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

Spring(07)——注入之lookup-method

7 單例注入多例之lookup-methodSpring有一種機制,可以動態的實現或重寫bean容器中指定bean的指定方法,然後將返回值指定為bean容器中的另一個bean。現針對前文提到的單例bea

Spring SingletonBeanRegistry Bean 註冊管理

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