2008-09-24 96 views
186

有沒有辦法在Spring應用程序中靜態/全局地請求ApplicationContext的副本?獲取Spring應用程序上下文

假設主類啓動並初始化應用程序上下文,它是否需要通過調用堆棧向下傳遞給需要它的任何類,或者有沒有類請求以前創建的上下文的方法? (?我以爲必須是單身)

回答

158

如果需要訪問容器的對象是容器中的bean,只需實現BeanFactoryAwareApplicationContextAware接口。

如果容器外部的對象需要訪問容器,我已經使用standard GoF singleton pattern作爲彈簧容器。這樣,你的應用程序中只有一個單例,其餘的都是容器中的單例bean。

+12

ApplicationContexts還有一個更好的接口 - ApplicationContextAware。如果你需要應用上下文功能,BeanFactoryAware應該可以工作,但你必須將它轉換到應用上下文。 – MetroidFan2002 2009-03-30 16:07:11

35

這裏是一個很好的方式(不是我的,原來的基準是在這裏: http://sujitpal.blogspot.com/2007/03/accessing-spring-beans-from-legacy-code.html

我用這個方法,並能正常工作基本上這是一個簡單的。豆

持有一個(靜態)參考應用程序上下文。通過在Spring配置它的初始化引用它。看看原來的裁判,這是非常清楚的。

+2

很好的博客文章有! – Chris 2010-07-19 04:44:54

+4

如果您在單元測試期間運行的代碼中調用`getBean`,則該方法可能會失敗,因爲在請求之前將不會設置Spring上下文。這是我在成功使用這種方法兩年後纔剛剛遭受衝擊的競賽狀況。 – HDave 2012-08-24 03:58:31

+0

我遇到了同樣的事情..不是從單元測試,而是從數據庫觸發器..任何建議? – 2017-04-15 19:14:59

4

看看ContextSingletonBeanFactoryLocator。這提供靜態訪問器來獲取Spring的上下文,假設他們是h已經以某種方式註冊了。

這不是很漂亮,也許比你想要的更復雜,但它的工作原理。

11

你執行任何其他建議之前,先問問自己這些問題...

  • 爲什麼我試圖讓ApplicationContext的?
  • 我是否有效地使用ApplicationContext作爲服務定位器?
  • 我可以避免訪問ApplicationContext嗎?

這些問題的答案在某些類型的應用程序(例如Web應用程序)中比在其他類型的應用程序中更容易,但無論如何都值得問。

訪問ApplicationContext確實違反了整個依賴注入原則,但有時你沒有太多的選擇。

18

我相信你可以使用SingletonBeanFactoryLocator。這個beanRefFactory.xml文件將保存實際的applicationContext,它會去是這樣的:

BeanFactoryLocator bfl = SingletonBeanFactoryLocator.getInstance(); 
BeanFactoryReference bf = bfl.useBeanFactory("mainContext"); 
SomeService someService = (SomeService) bf.getFactory().getBean("someService"); 

<bean id="mainContext" class="org.springframework.context.support.ClassPathXmlApplicationContext"> 
    <constructor-arg> 
     <list> 
      <value>../applicationContext.xml</value> 
     </list> 
    </constructor-arg> 
</bean> 

和代碼,以從徘徊無論會是這樣的ApplicationContext中取得bean Spring團隊不鼓勵使用這個類和yadayada,但它適合我使用它的地方。

6

如果您使用web應用程序,還有另一種方式來訪問應用程序上下文,而不使用servletfilter和ThreadLocal使用單例。在過濾器中,您可以使用WebApplicationContextUtils訪問應用程序上下文,並將應用程序上下文或所需的Bean存儲在TheadLocal中。

注意:如果您忘記取消設置ThreadLocal,則在嘗試取消部署應用程序時會遇到令人討厭的問題!因此,你應該設置它並立即開始一個嘗試,在最後部分中取消設置ThreadLocal。

當然,這仍然使用單例:ThreadLocal。但實際的bean不再需要了。甚至可以是請求範圍的,如果在EAR中有libaries的應用程序中有多個WAR,該解決方案也可以工作。不過,你可能會認爲這種使用ThreadLocal與使用純單例一樣糟糕。 ;-)

也許Spring已經提供了類似的解決方案?我沒有找到,但我不確定。

108

您可以實現ApplicationContextAware或只使用@Autowired

public class SpringBean { 
    @Autowired 
    private ApplicationContext appContext; 
} 

SpringBean將有ApplicationContext注入,其內這個bean實例化。例如,如果你有一個非常標準的背景層次的Web應用程序:

main application context <- (child) MVC context 

SpringBean的主要範圍內聲明的,它會注入的主要方面;否則,如果在MVC上下文中聲明它,則會注入MVC​​上下文。

2

請注意,通過將當前ApplicationContextApplicationContext本身的任何狀態存儲在一個靜態變量中(例如使用單例模式),如果使用Spring測試,則會使測試變得不穩定且不可預知。這是因爲Spring-test在同一個JVM中緩存和重用應用程序上下文。例如:

  1. 測試運行並用@ContextConfiguration({"classpath:foo.xml"})註釋。
  2. 測試B的運行,並標註有@ContextConfiguration({"classpath:foo.xml", "classpath:bar.xml})
  3. 測試C運行,並標註有@ContextConfiguration({"classpath:foo.xml"})

當測試A運行,一個ApplicationContext被創建,任何豆implemeting ApplicationContextAware或自動裝配ApplicationContext可能會寫到靜態變量。

當試驗B運行同樣的事情發生,現在的靜態變量指向測試B的ApplicationContext

當試驗C運行,沒有豆創建作爲TestContext(以及本文所述ApplicationContext)從試驗A被重新使用。現在你有一個靜態變量指向另一個ApplicationContext比當前持有這個bean的測試。

0

請注意,下面的代碼將創建新的應用程序上下文,而不是使用已經加載的代碼。

private static final ApplicationContext context = 
       new ClassPathXmlApplicationContext("beans.xml"); 

還要注意的是beans.xml應該是在戰爭src/main/resources手段的一部分它是WEB_INF/classes,其中作爲真正的應用程序將通過在Web.xml提到applicationContext.xml加載部分。

<context-param> 
    <param-name>contextConfigLocation</param-name> 
    <param-value>META-INF/spring/applicationContext.xml</param-value> 
</context-param> 

這是difficult提到在ClassPathXmlApplicationContext構造applicationContext.xml路徑。 ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml")將無法​​找到該文件。

因此,最好通過使用註釋來使用現有的applicationContext。

@Component 
public class OperatorRequestHandlerFactory { 

    public static ApplicationContext context; 

    @Autowired 
    public void setApplicationContext(ApplicationContext applicationContext) { 
     context = applicationContext; 
    } 
} 
1
SpringApplicationContext.java 

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

/** 
* Wrapper to always return a reference to the Spring Application 
Context from 
* within non-Spring enabled beans. Unlike Spring MVC's 
WebApplicationContextUtils 
* we do not need a reference to the Servlet context for this. All we need is 
* for this bean to be initialized during application startup. 
*/ 
public class SpringApplicationContext implements 
ApplicationContextAware { 

    private static ApplicationContext CONTEXT; 

    /** 
    * This method is called from within the ApplicationContext once it is 
    * done starting up, it will stick a reference to itself into this bean. 
    * @param context a reference to the ApplicationContext. 
    */ 
    public void setApplicationContext(ApplicationContext context) throws BeansException { 
    CONTEXT = context; 
    } 

    /** 
    * This is about the same as context.getBean("beanName"), except it has its 
    * own static handle to the Spring context, so calling this method statically 
    * will give access to the beans by name in the Spring application context. 
    * As in the context.getBean("beanName") call, the caller must cast to the 
    * appropriate target class. If the bean does not exist, then a Runtime error 
    * will be thrown. 
    * @param beanName the name of the bean to get. 
    * @return an Object reference to the named bean. 
    */ 
    public static Object getBean(String beanName) { 
    return CONTEXT.getBean(beanName); 
    } 
} 

來源:http://sujitpal.blogspot.de/2007/03/accessing-spring-beans-from-legacy-code.html

相關問題