programing

스프링 응용 프로그램 컨텍스트 가져오기

prostudy 2022. 4. 29. 23:13
반응형

스프링 응용 프로그램 컨텍스트 가져오기

Spring 애플리케이션에서 ApplicationContext 사본을 정적으로/광학적으로 요청할 수 있는 방법이 있는가?

메인 클래스가 시작되어 애플리케이션 컨텍스트를 초기화한다고 가정할 때, 콜 스택을 통해 해당 클래스를 필요한 클래스로 전달할 필요가 있는가, 아니면 클래스가 이전에 만든 컨텍스트를 요청할 수 있는 방법이 있는가?(어느 것이 싱글톤이어야 한다고 생각하나?)

컨테이너에 대한 액세스가 필요한 객체가 컨테이너의 빈인 경우 BeanFactoryAware 또는 ApplicationContextAware 인터페이스를 구현하십시오.

컨테이너 외부에 있는 물체가 컨테이너에 접근해야 한다면, 나는 스프링 컨테이너에 표준 GoF 싱글톤 패턴을 사용해 본 적이 있다.그렇게 하면 신청서에 싱글톤이 한 개뿐이고 나머지는 모두 싱글톤 콩이 용기에 담겨 있다.

구현할 수 있음ApplicationContextAware아니면 그냥 사용해라.@Autowired:

public class SpringBean {
  @Autowired
  private ApplicationContext appContext;
}

SpringBean을 가질 것이다ApplicationContext이 콩은 그 안에서 즉석 처리된다.예를 들어 표준 컨텍스트 계층 구조를 가진 웹 응용 프로그램이 있는 경우:

main application context <- (child) MVC context

그리고SpringBean주 컨텍스트 내에서 선언되고, 주 컨텍스트가 주입되며, 그렇지 않으면 MVC 컨텍스트 내에서 선언되면 MVC 컨텍스트가 주입된다.

여기 좋은 방법이 있다(내 것이 아니라, 원래 참고문헌은 여기 있다: http://sujitpal.blogspot.com/2007/03/accessing-spring-beans-from-legacy-code.html

나는 이 접근법을 사용했고 그것은 잘 작동한다.기본적으로 애플리케이션 컨텍스트에 대한 (정적) 참조를 보관하는 간단한 콩입니다.스프링 구성에서 참조하면 초기화된다.

원본 참고 자료 좀 봐, 아주 명확해.

나는 네가 싱글톤 빈 팩토리 로케이터를 사용할 수 있다고 믿는다.beanRefFactory.xml 파일에는 실제 applicationContext가 저장되며 다음과 같은 작업이 수행된다.

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

그리고 애플리케이션 컨텍스트에서 whreever로부터 콩을 얻는 코드는 다음과 같은 것일 것이다.

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

스프링 팀은 이 수업과 야다야다의 사용을 단념시키지만, 그것은 내가 그것을 사용했던 곳에 잘 들어맞았다.

다른 제안을 실행하기 전에 스스로에게 질문하십시오.

  • ApplicationContext를 가져오는 이유
  • ApplicationContext를 효과적으로 서비스 로케이터로 사용하고 있는가?
  • ApplicationContext에 액세스하는 것을 피할 수 있는가?

이러한 질문에 대한 답은 특정 유형의 애플리케이션(예: 웹 앱)에서 다른 애플리케이션보다 쉽지만, 어쨌든 물어볼 가치가 있다.

ApplicationContext에 액세스하는 것은 일종의 전체 종속성 주입 원칙을 위반하는 것이지만, 가끔은 선택의 여지가 많지 않을 때도 있다.

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

웹 앱을 사용하는 경우 서블릿필터와 스레드로컬을 사용하여 단골격을 사용하지 않고 응용 프로그램 컨텍스트에 액세스할 수 있는 다른 방법도 있다.필터에서 WebApplicationContextUtils를 사용하여 응용 프로그램 컨텍스트에 액세스하고 응용 프로그램 컨텍스트 또는 필요한 콩을 TheadLocal에 저장할 수 있다.

주의: ThreadLocal 설정 해제를 잊어버린 경우 응용 프로그램 배포를 취소하려고 할 때 심각한 문제가 발생할 수 있음!따라서, 당신은 그것을 설정하고, 최종 파트에서 스레드 로컬을 해제하는 시도를 즉시 시작해야 한다.

물론, 이것은 여전히 싱글톤인 스레드 로컬을 사용한다.그러나 실제 콩은 더 이상 필요하지 않다.이 솔루션은 요청 범위도 될 수 있으며, EAR에 libaries가 있는 애플리케이션에서 여러 개의 WAR이 있는 경우에도 작동한다.그래도 이 스레드로컬의 사용은 평편한 단골격의 사용만큼 나쁘다고 생각할 수 있다. ;;-)

아마도 스프링은 이미 비슷한 해결책을 제공했을까?한 마리도 못 찾았는데 확실히는 모르겠어.

ContextSingletonBeanFactoryLocator를 살펴보십시오.Spring의 컨텍스트가 특정 방식으로 등록되었다고 가정할 때, 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)응용 프로그램콘텍스트를 가져올 방법

ApplicationContextAware:

실행 중인 ApplicationContext에 대해 알림을 받고자 하는 모든 개체에 의해 구현되는 인터페이스.예를 들어, 이 인터페이스를 구현하는 것은 물체가 협력하는 콩 집합에 대한 액세스를 필요로 할 때 타당하다.

  1. 자동 만료:

    @Autowired
    private ApplicationContext applicationContext;
    

여기@Autowired키워드는 applicationContext를 제공한다.autorwided는 문제가 있다.그것은 유닛 테스트 중에 문제를 일으킬 것이다.

전류에서 임의의 상태를 저장함으로써ApplicationContext또는ApplicationContext정적 변수(예: 싱글톤 패턴 사용)에 있는 그 자체로 스프링 테스트를 사용할 경우 테스트가 불안정하고 예측할 수 없게 된다.이는 스프링 테스트가 동일한 JVM에서 애플리케이션 컨텍스트를 캐슁하고 재사용하기 때문이다.예를 들면 다음과 같다.

  1. 테스트 A 런에 주석을 달면@ContextConfiguration({"classpath:foo.xml"}).
  2. 테스트 B 실행 및 주석을 달음@ContextConfiguration({"classpath:foo.xml", "classpath:bar.xml})
  3. 테스트 C 실행 및 주석을 달음@ContextConfiguration({"classpath:foo.xml"})

테스트 A가 실행되면ApplicationContext생성되고, 모든 콩이 파괴된다.ApplicationContextAware또는 자동 상속ApplicationContext정적 변수에 쓸 수 있다.

테스트 B가 실행될 때 동일한 현상이 발생하며, 현재 정적 변수는 테스트 B를 가리킨다.ApplicationContext

테스트 C가 실행되면 콩이 생성되지 않는다.TestContext(그리고 여기서)ApplicationContextA 테스트의 )는 재사용된다.이제 정적 변수가 다른 변수를 가리키고 있음ApplicationContext네 시험을 위해 콩을 들고 있는 사람보다 더 많이 말이야.

이것이 얼마나 유용한지는 확실하지 않지만, 앱을 초기화할 때 컨텍스트를 얻을 수도 있다.이것은 문맥이 생기기 전에라도 가장 빨리 얻을 수 있는 것이다.@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);

다음과 같이 스프링 빈에서 자동 실행: @Autorward private ApplicationContext appContext;

응용 프로그램 컨텍스트 오브젝트가 될 것이다.

나는 나의 싱글톤 스프링 콩에 외부 접근을 허용하기 위해 간단하고 표준화된 방법을 사용한다.이 방법으로, 나는 봄을 계속해서 빈을 인스턴스화시킨다.내가 하는 일은 다음과 같다.

  1. 엔클로저 클래스와 동일한 유형의 개인 정적 변수를 정의하십시오.
  2. 이 변수를 다음으로 설정this가 없는 경우 변수클래스에 생성자가 없는 경우 변수를 설정할 기본 생성자를 추가하십시오.
  3. 싱글톤 변수를 반환하는 공개 정적 게터 방법을 정의하십시오.

예를 들면 다음과 같다.

@Component
public class MyBean {
    ...

    private static MyBean singleton = null;

    public MyBean() {
        ...
        singleton = this;
    }

    ...
    
    public void someMethod() {
        ...
    }

    ...

    public static MyBean get() {
        return singleton;
    }
}

그러면 나는 전화할 수 있다.someMethod내 코드로 어디든, 단돈콩으로, 다음을 통해:

MyBean.get().someMethod();

이미 하위 클래스를 사용하고 있는 경우ApplicationContext이 메커니즘을 직접 여기에 추가할 수 있다.그렇지 않으면, 단지 이것을 하기 위해 그것을 하위 분류하거나, 또는 이 메커니즘을 콩에 접근할 수 있는 모든 콩에 추가할 수 있다.ApplicationContext, 그리고 나서 에 접근하기 위해 그것을 사용한다.ApplicationContext어디서든중요한 것은 봄의 환경에 들어갈 수 있게 해주는 이 메커니즘이라는 것이다.

아래 코드는 이미 로드된 응용 프로그램 컨텍스트를 사용하는 대신 새 응용 프로그램 컨텍스트를 생성한다는 점에 유의하십시오.

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

또한 이 점에 유의하십시오.beans.xml의 일부여야 한다src/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.xml에 들어가다ClassPathXmlApplicationContext건설업자 ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml")파일을 찾을 수 없을 것이다.

따라서 주석을 사용하여 기존 applicationContext를 사용하는 것이 좋다.

@Component
public class OperatorRequestHandlerFactory {

    public static ApplicationContext context;

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

접근법 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: 스프링 관리 콩에서 응용 프로그램 컨텍스트를 자동 저장하십시오.

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

참조 링크.

나는 이 질문에 대한 답을 알고 있지만, 스프링 컨텍스트를 되찾기 위해 내가 했던 코틀린 코드를 공유하고 싶다.

나는 전문가가 아니기 때문에 비평가, 평론, 조언에 개방적이다.

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;
        }
    }
}

이제 스프링 컨텍스트를 공개적으로 사용할 수 있어 이 자바 서블릿에서와 같이 컨텍스트(junit tests, bean, 수동으로 인스턴스화한 클래스)와 독립적으로 동일한 방법을 호출할 수 있다.

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


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


    public MyWebServlet() {

    }
}

@Autowire 클래스가 RestController 또는 Configuration Class가 아닌 경우에도 applicationContext 개체가 null로 다가오고 있었다.아래 클래스를 사용하여 새 클래스를 만들려고 시도했지만 정상 작동 중:

@Component
public class SpringContext implements ApplicationContextAware{

   private static ApplicationContext applicationContext;

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

그런 다음 다음과 같은 방법으로 구현된 클래스 참조를 얻는 것과 같은 필요에 따라 동일한 클래스에서 게터 방법을 구현할 수 있다.

    applicationContext.getBean(String serviceName,Interface.Class)

참조URL: https://stackoverflow.com/questions/129207/getting-spring-application-context

반응형