Springアプリケーションコンテキストの取得


216

SpringアプリケーションでApplicationContextのコピーを静的/グローバルにリクエストする方法はありますか?

メインクラスが起動してアプリケーションコンテキストを初期化すると仮定すると、それをコールスタックを介してそれを必要とするクラスに渡す必要がありますか、またはクラスが以前に作成されたコンテキストを要求する方法はありますか?(私はシングルトンでなければならないと思いますか?)

回答:


171

コンテナーへのアクセスが必要なオブジェクトがコンテナー内のBeanである場合は、BeanFactoryAwareまたはApplicationContextAwareインターフェースを実装するだけです。

コンテナー外のオブジェクトがコンテナーにアクセスする必要がある場合は、Springコンテナーに標準のGoFシングルトンパターンを使用しました。このように、アプリケーションにはシングルトンが1つだけあり、残りはすべてコンテナー内のシングルトンBeanです。


15
ApplicationContextsのより良いインターフェース-ApplicationContextAwareもあります。BeanFactoryAwareは機能するはずですが、アプリケーションコンテキスト機能が必要な場合は、アプリケーションコンテキストにキャストする必要があります。
MetroidFan2002 2009年

@Don Kirkbyシングルトンパターンを使用することは、コンテナークラス内の静的メソッドからコンテナークラスをインスタンス化することを意味します...オブジェクトを「手動で」インスタンス化すると、Springによって管理されなくなります。この問題にどのように取り組みましたか?
アントニン

@Antoninの9年後、私の記憶は少しあいまいですが、シングルトンがSpringコンテナー内で管理されたとは思いません。シングルトンの唯一の仕事は、XMLファイルからコンテナーを読み込み、それを静的メンバー変数に保持することでした。独自のクラスのインスタンスを返すのではなく、Springコンテナのインスタンスを返しました。
Don Kirkby

1
それ自体への静的参照を所有するSpringシングルトンであるDon Kirkbyに感謝します。したがって、おそらく非Springオブジェクトで使用できます。
Antonin

シングルトンのinstance()メソッドをファクトリとして使用するようにSpringコンテナに指示した場合は、@ Antoninでうまくいくかもしれません。ただし、私は、コンテナーの外部にあるすべてのコードが最初にコンテナーにアクセスできるようにするだけだと思います。次に、そのコードはコンテナからオブジェクトをリクエストできます。
Don Kirkby

118

実装するApplicationContextAwareか、単に使用することができます@Autowired

public class SpringBean {
  @Autowired
  private ApplicationContext appContext;
}

SpringBeanきますApplicationContext。このBeanがインスタンス化された範囲内で、注入されました。たとえば、かなり標準的なコンテキスト階層を持つWebアプリケーションがあるとします。

main application context <- (child) MVC context

そしてSpringBeanメインのコンテキスト内で宣言され、それがメインのコンテキストが注入されています。それ以外の場合、MVCコンテキスト内で宣言されていると、MVCコンテキストが挿入されます。


2
これは束を助けました。Spring 2.0の古いアプリで奇妙な問題がいくつかあります。あなたの答えは、単一のSpring IoCコンテナで単一のApplicationContextを正しく機能させる唯一の方法でした。
Stu Thompson、

1
Readers ..このSpringBeanをspringconfig.xmlでBeanとして宣言することを忘れないでください。
超新星

これがすでにBeanであり、新しいXXXXApplicationContext(XXXX)のインスタンスを返すApplication.getApplicationContext()(Singleton pattern)を使用するとどうなるのですか?なぜ自動配線する必要があるのですか?
Jaskey、2014年

あなたも使用@Injectできます
Alireza Fattahi 2015年

39

これは良い方法です(私のものではなく、元の参照はここにあります:http : //sujitpal.blogspot.com/2007/03/accessing-spring-beans-from-legacy-code.html

私はこのアプローチを使用しましたが、うまく機能します。基本的には、アプリケーションコンテキストへの(静的)参照を保持する単純なBeanです。Spring設定でそれを参照することにより、初期化されます。

元の参照を見てください。非常に明確です。


4
getBeanユニットテスト中に実行するコードから呼び出す場合、要求する前にSpringコンテキストが設定されないため、このアプローチは失敗する可能性があります。このアプローチを2年間成功裏に使用してから今日に至ったレースコンディション。
HDave 2012

私は同じことを実行しています。単体テストからではなく、データベーストリガーから実行しています。
John Deverall

優れた反応。ありがとうございました。
サグネタ

17

SingletonBeanFactoryLocatorを使用できると思います。beanRefFactory.xmlファイルは実際のapplicationContextを保持し、次のようになります。

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

そして、アプリケーションコンテキストからBeanを取得するコードは、次のようになります。

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

Springチームは、このクラスとyadayadaの使用を推奨していませんが、私がそれを使用したところにはとても適しています。


11

他の提案を実装する前に、次の質問を自問してください...

  • なぜApplicationContextを取得しようとしているのですか?
  • ApplicationContextをサービスロケーターとして効果的に使用していますか?
  • ApplicationContextへのアクセスをまったく回避できますか?

これらの質問に対する回答は、特定の種類のアプリケーション(Webアプリなど)の方が他の種類の場合よりも簡単ですが、とにかく質問する価値があります。

ApplicationContextへのアクセスは、依存関係注入の原則全体にある程度違反しますが、多くの選択肢がない場合があります。


5
良い例がJSPタグです。それらの作成はサーブレットコンテナによって管理されるため、静的にコンテキストを取得する以外に選択肢はありません。Springはベースタグクラスを提供し、それらはBeanFactoryLocatorsを使用して必要なコンテキストを取得します。
skaffman 2008

6

Webアプリを使用する場合は、サーブレットフィルターとThreadLocalを使用して、シングルトンを使用せずにアプリケーションコンテキストにアクセスする別の方法もあります。フィルターでは、WebApplicationContextUtilsを使用してアプリケーションコンテキストにアクセスし、アプリケーションコンテキストまたは必要なBeanをTheadLocalに保存できます。

注意:ThreadLocalの設定を解除するのを忘れると、アプリケーションをアンデプロイしようとすると厄介な問題が発生します。したがって、それを設定して、すぐに、finally-partのThreadLocalの設定を解除する試行を開始する必要があります。

もちろん、これはまだシングルトンを使用しています:ThreadLocal。しかし、実際の豆はもう必要ありません。はリクエストスコープにすることもでき、このソリューションは、EARのライブラリを使用するアプリケーションに複数のWARがある場合にも機能します。それでも、このThreadLocalの使用は、単純なシングルトンの使用と同じくらい悪いと考えるかもしれません。;-)

おそらくSpringはすでに同様のソリューションを提供していますか?見つかりませんでしたが、よくわかりません。


6
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



4

Springアプリケーションでアプリケーションコンテキストを取得するには多くの方法があります。それらは以下のとおりです。

  1. ApplicationContextAware経由

    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    
    public class AppContextProvider implements ApplicationContextAware {
    
    private ApplicationContext applicationContext;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    }

ここで、setApplicationContext(ApplicationContext applicationContext)メソッドはapplicationContextを取得します

ApplicationContextAware

実行するApplicationContextの通知を希望するオブジェクトによって実装されるインターフェース。このインターフェースの実装は、たとえば、オブジェクトが一連の共同Beanへのアクセスを必要とする場合に意味があります。

  1. Autowired経由

    @Autowired
    private ApplicationContext applicationContext;

ここで、@AutowiredキーワードはapplicationContextを提供します。Autowiredに問題があります。単体テスト中に問題が発生します。


3

たとえば、シングルトンパターンを使用して、現在の状態ApplicationContextまたはApplicationContextそれ自体を静的変数に格納すると、Spring-testを使用している場合、テストが不安定になり、予測不可能になることに注意してください。これは、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が作成され、実装ApplicationContextAwareまたは自動配線を行うBean ApplicationContextが静的変数に書き込む場合があります。

テストBを実行すると同じことが起こり、静的変数がテストBを指すようになります。 ApplicationContext

テストCが実行れると、テストAのTestContext(ここではApplicationContextが再利用されるため、Beanは作成さません。これApplicationContextで、テスト用のBeanを現在保持している変数とは別の変数を指す静的変数が取得されました。


1

これがどれほど役立つかわかりませんが、アプリを初期化するときにコンテキストを取得することもできます。これは、の前であっても、コンテキストを取得できる最も早い時刻です@Autowire

@SpringBootApplication
public class Application extends SpringBootServletInitializer {
    private static ApplicationContext context;

    // I believe this only runs during an embedded Tomcat with `mvn spring-boot:run`. 
    // I don't believe it runs when deploying to Tomcat on AWS.
    public static void main(String[] args) {
        context = SpringApplication.run(Application.class, args);
        DataSource dataSource = context.getBean(javax.sql.DataSource.class);
        Logger.getLogger("Application").info("DATASOURCE = " + dataSource);

0

その点に注意してください; 以下のコードは、すでにロードされているものを使用する代わりに、新しいアプリケーションコンテキストを作成します。

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

また、戦争beans.xmlsrc/main/resources手段の一部である必要があることに注意してください。WEB_INF/classes実際のアプリケーションはでapplicationContext.xml言及されているとおりにロードされWeb.xmlます。

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

コンストラクタでパスに言及することは困難です。ファイルを見つけることができません。applicationContext.xmlClassPathXmlApplicationContextClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml")

したがって、アノテーションを使用して既存のapplicationContextを使用することをお勧めします。

@Component
public class OperatorRequestHandlerFactory {

    public static ApplicationContext context;

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        context = applicationContext;
    }
}

0

この質問への回答はわかっていますが、Spring Contextを取得するために実行したKotlinコードを共有したいと思います。

私は専門家ではないので、批評家、レビュー、アドバイスを受け入れます。

https://gist.github.com/edpichler/9e22309a86b97dbd4cb1ffe011aa69dd

package com.company.web.spring

import com.company.jpa.spring.MyBusinessAppConfig
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
import org.springframework.stereotype.Component
import org.springframework.web.context.ContextLoader
import org.springframework.web.context.WebApplicationContext
import org.springframework.web.context.support.WebApplicationContextUtils
import javax.servlet.http.HttpServlet

@Configuration
@Import(value = [MyBusinessAppConfig::class])
@ComponentScan(basePackageClasses  = [SpringUtils::class])
open class WebAppConfig {
}

/**
 *
 * Singleton object to create (only if necessary), return and reuse a Spring Application Context.
 *
 * When you instantiates a class by yourself, spring context does not autowire its properties, but you can wire by yourself.
 * This class helps to find a context or create a new one, so you can wire properties inside objects that are not
 * created by Spring (e.g.: Servlets, usually created by the web server).
 *
 * Sometimes a SpringContext is created inside jUnit tests, or in the application server, or just manually. Independent
 * where it was created, I recommend you to configure your spring configuration to scan this SpringUtils package, so the 'springAppContext'
 * property will be used and autowired at the SpringUtils object the start of your spring context, and you will have just one instance of spring context public available.
 *
 *Ps: Even if your spring configuration doesn't include the SpringUtils @Component, it will works tto, but it will create a second Spring Context o your application.
 */
@Component
object SpringUtils {

        var springAppContext: ApplicationContext? = null
    @Autowired
    set(value) {
        field = value
    }



    /**
     * Tries to find and reuse the Application Spring Context. If none found, creates one and save for reuse.
     * @return returns a Spring Context.
     */
    fun ctx(): ApplicationContext {
        if (springAppContext!= null) {
            println("achou")
            return springAppContext as ApplicationContext;
        }

        //springcontext not autowired. Trying to find on the thread...
        val webContext = ContextLoader.getCurrentWebApplicationContext()
        if (webContext != null) {
            springAppContext = webContext;
            println("achou no servidor")
            return springAppContext as WebApplicationContext;
        }

        println("nao achou, vai criar")
        //None spring context found. Start creating a new one...
        val applicationContext = AnnotationConfigApplicationContext ( WebAppConfig::class.java )

        //saving the context for reusing next time
        springAppContext = applicationContext
        return applicationContext
    }

    /**
     * @return a Spring context of the WebApplication.
     * @param createNewWhenNotFound when true, creates a new Spring Context to return, when no one found in the ServletContext.
     * @param httpServlet the @WebServlet.
     */
    fun ctx(httpServlet: HttpServlet, createNewWhenNotFound: Boolean): ApplicationContext {
        try {
            val webContext = WebApplicationContextUtils.findWebApplicationContext(httpServlet.servletContext)
            if (webContext != null) {
                return webContext
            }
            if (createNewWhenNotFound) {
                //creates a new one
                return ctx()
            } else {
                throw NullPointerException("Cannot found a Spring Application Context.");
            }
        }catch (er: IllegalStateException){
            if (createNewWhenNotFound) {
                //creates a new one
                return ctx()
            }
            throw er;
        }
    }
}

これで、Springコンテキストが公開され、このJavaサーブレットのように、コンテキスト(junitテスト、Bean、手動でインスタンス化されたクラス)に関係なく同じメソッドを呼び出すことができます。

@WebServlet(name = "MyWebHook", value = "/WebHook")
public class MyWebServlet extends HttpServlet {


    private MyBean byBean
            = SpringUtils.INSTANCE.ctx(this, true).getBean(MyBean.class);


    public MyWebServlet() {

    }
}

0

以下のようにSpring Beanでautowireを実行します。@Autowired private ApplicationContext appContext;

あなたはapplicationcontextオブジェクトになります。


0

アプローチ1: ApplicationContextAwareインターフェースを実装することにより、ApplicationContextを注入できます。参照リンク

@Component
public class ApplicationContextProvider implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

アプローチ2:任意のSpringマネージドBeanのAutowireアプリケーションコンテキスト。

@Component
public class SpringBean {
  @Autowired
  private ApplicationContext appContext;
}

参照リンク

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.