스프링 애플리케이션 컨텍스트 얻기


216

Spring 애플리케이션에서 ApplicationContext의 사본을 정적으로 / 전역 적으로 요청하는 방법이 있습니까?

메인 클래스가 응용 프로그램 컨텍스트를 시작하고 초기화한다고 가정하면 호출 스택을 통해 클래스를 필요로하는 클래스로 전달해야합니까, 아니면 클래스가 이전에 만든 컨텍스트를 요청하는 방법이 있습니까? (내가 싱글 톤이어야한다고 가정 하는가?)

답변:


171

컨테이너에 액세스해야하는 오브젝트가 컨테이너의 Bean 인 경우 BeanFactoryAware 또는 ApplicationContextAware 인터페이스를 구현하십시오 .

컨테이너 외부의 객체가 컨테이너에 액세스해야하는 경우 스프링 컨테이너에 표준 GoF 싱글 톤 패턴 을 사용했습니다 . 이렇게하면 응용 프로그램에 단일 톤 만 있고 나머지는 컨테이너의 단일 콩입니다.


15
ApplicationContexts-ApplicationContextAware에 대한 더 나은 인터페이스도 있습니다. BeanFactoryAware는 작동하지만 앱 컨텍스트 기능이 필요한 경우이를 애플리케이션 컨텍스트로 캐스트해야합니다.
MetroidFan2002

@Don Kirkby 싱글 톤 패턴을 사용한다는 것은 컨테이너 클래스 내의 정적 메소드에서 컨테이너 클래스를 설정하는 것을 의미합니다. 일단 객체를 "수동으로"인스턴스화하면 더 이상 Spring에서 관리하지 않습니다.이 문제를 어떻게 해결 했습니까?
Antonin

@Antonin은 9 년 후 기억이 약간 모호하지만, Singleton이 Spring 컨테이너 내에서 관리되지 않았다고 생각합니다. 싱글 톤의 유일한 일은 XML 파일에서 컨테이너를로드하고 정적 멤버 변수에 보관하는 것입니다. 자체 클래스의 인스턴스를 반환하지 않고 Spring 컨테이너의 인스턴스를 반환했습니다.
돈 커크비

1
감사합니다. Don Kirkby, Spring 싱글 톤은 자체적으로 정적 참조를 소유하므로 Spring 이외의 객체에서 사용할 수 있습니다.
Antonin

스프링 컨테이너에 싱글 톤의 instance()방법을 팩토리로 사용하도록 지시하면 @Antonin이 작동 할 수 있습니다 . 그러나 컨테이너 외부의 모든 코드가 컨테이너에 먼저 액세스하도록 허용한다고 생각합니다. 그런 다음 해당 코드는 컨테이너에서 객체를 요청할 수 있습니다.
돈 커크비

118

구현 ApplicationContextAware하거나 사용할 수 있습니다 @Autowired.

public class SpringBean {
  @Autowired
  private ApplicationContext appContext;
}

SpringBeanApplicationContext이 빈이 인스턴스화하는 내 주입. 예를 들어, 표준 컨텍스트 계층 구조를 갖춘 웹 애플리케이션이있는 경우 :

main application context <- (child) MVC context

그리고 SpringBean,이 주사 메인 문맥을 갖 메인 문맥 내에서 선언되고; 그렇지 않으면 MVC 컨텍스트 내에서 선언 된 경우 MVC 컨텍스트가 주입됩니다.


2
이것은 무리를 도왔습니다. Spring 2.0을 사용하는 구형 앱에서 이상한 문제가 발생했으며 단일 Spring IoC 컨테이너를 사용하여 단일 ApplicationContext로 작업 할 수있는 유일한 방법이었습니다.
Stu Thompson

1
독자 .. springconfig.xml에서이 SpringBean을 Bean으로 선언하는 것을 잊지 마십시오.
초신성

이것이 이미 Bean이고 새로운 XXXXApplicationContext (XXXX)의 인스턴스를 반환하는 Application.getApplicationContext () (Singleton pattern)을 사용하면 왜 작동하지 않습니까? 왜 자동 배선을해야합니까?
Jaskey

당신은 사용할 수 @Inject
알리레자 Fattahi

39

여기 좋은 방법이 있습니다 (원본 참조는 여기에 있습니다 : http://sujitpal.blogspot.com/2007/03/accessing-spring-beans-from-legacy-code.html

나는이 접근법을 사용했으며 잘 작동합니다. 기본적으로 응용 프로그램 컨텍스트에 대한 (정적) 참조를 보유하는 간단한 Bean입니다. 스프링 설정에서 참조하면 초기화됩니다.

원래 심판을 살펴보십시오. 매우 명확합니다.


4
getBean요청하기 전에 Spring 컨텍스트가 설정되지 않기 때문에 단위 테스트 중에 실행되는 코드에서 호출하면이 방법이 실패 할 수 있습니다 . 이 접근법을 성공적으로 사용한 지 2 년 만에 오늘 막 경쟁 상황에 처했습니다.
HDave

단위 테스트가 아닌 데이터베이스 트리거에서 동일한 문제가 발생합니다. 제안 사항이 있습니까?
John Deverall

탁월한 반응. 감사합니다.
sagneta

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에 전혀 액세스하지 않아도 되나요?

이러한 질문에 대한 답변은 특정 유형의 응용 프로그램 (예 : 웹 응용 프로그램)에서 다른 응용 프로그램보다 더 쉽지만 어쨌든 물어볼 가치가 있습니다.

ApplicationContext에 액세스하는 것은 전체 의존성 주입 원칙을 위반하지만 때로는 선택의 여지가없는 경우가 있습니다.


5
좋은 예는 JSP 태그입니다. 그것들은 서블릿 컨테이너에 의해 만들어 지므로 정적으로 컨텍스트를 얻는 것 외에는 선택의 여지가 없습니다. Spring은 기본 Tag 클래스를 제공하며 BeanFactoryLocators를 사용하여 필요한 컨텍스트를 얻습니다.
skaffman

6

웹 응용 프로그램을 사용하는 경우 서블릿 필터와 ThreadLocal을 사용하여 싱글 톤을 사용하지 않고 응용 프로그램 컨텍스트에 액세스하는 또 다른 방법이 있습니다. 필터에서 WebApplicationContextUtils를 사용하여 애플리케이션 컨텍스트에 액세스하고 애플리케이션 컨텍스트 또는 필요한 Bean을 TheadLocal에 저장할 수 있습니다.

주의 : ThreadLocal을 설정 해제하는 것을 잊어 버린 경우 응용 프로그램 배포를 취소 할 때 문제가 발생합니다! 따라서 마지막 부분에서 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
    private ApplicationContext applicationContext;

여기서 @Autowired키워드는 applicationContext를 제공합니다. 자동 유선에 문제가 있습니다. 단위 테스트 중에 문제가 발생합니다.


3

현재 ApplicationContext또는 ApplicationContext자체 의 상태를 정적 변수 (예 : 싱글 톤 패턴 사용)로 저장하면 스프링 테스트를 사용하는 경우 테스트가 불안정하고 예측할 수 없게됩니다. Spring 테스트는 동일한 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 의 (및 여기에서 )가 재사용 될 때 Bean이 작성되지 않습니다 . 이제 테스트를 위해 빈을 현재 보유하고있는 변수 이외의 것을 가리키는 정적 변수가 생겼습니다 .TestContextApplicationContextApplicationContext


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.xml일부 가 되어야한다는 점에 유의하십시오 .src/main/resourcesWEB_INF/classesapplicationContext.xmlWeb.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;
        }
    }
}

이제 스프링 컨텍스트를 공개적으로 사용할 수 있으며이 Java Servlet에서와 같이 컨텍스트 (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 : 모든 스프링 관리 Bean에서 Autowire Application 컨텍스트.

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

참조 링크 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.