Вопрос:

Получение Spring Application Context

java spring configuration

296435 просмотра

14 ответа

11400 Репутация автора

Есть ли способ статически / глобально запросить копию ApplicationContext в приложении Spring?

Предполагая, что основной класс запускается и инициализирует контекст приложения, нужно ли передавать его через стек вызовов любым классам, которые в этом нуждаются, или есть ли у класса способ запросить ранее созданный контекст? (Который я предполагаю, должен быть синглтоном?)

Автор: Joe Skora Источник Размещён: 24.09.2008 07:10

Ответы (14)


37 плюса

39236 Репутация автора

Вот хороший способ (не мой, оригинальная ссылка здесь: http://sujitpal.blogspot.com/2007/03/accessing-spring-beans-from-legacy-code.html

Я использовал этот подход, и он отлично работает. По сути, это простой компонент, содержащий (статическую) ссылку на контекст приложения. Ссылка на него в весеннем конфиге инициализируется.

Посмотрите на оригинальную ссылку, это очень ясно.

Автор: Steve B. Размещён: 24.09.2008 07:33

4 плюса

342517 Репутация автора

Взгляните на ContextSingletonBeanFactoryLocator . Он предоставляет статические средства доступа для получения контекста Spring, предполагая, что они были зарегистрированы определенным образом.

Это не красиво, и сложнее, чем, возможно, вы хотели бы, но это работает.

Автор: skaffman Размещён: 24.09.2008 07:36

165 плюса

28145 Репутация автора

Решение

Если объект, которому требуется доступ к контейнеру, является компонентом в контейнере, просто реализуйте интерфейсы BeanFactoryAware или ApplicationContextAware .

Если объект вне контейнера нуждается в доступе к контейнеру, я использовал стандартный шаблон синглтона GoF для контейнера пружины. Таким образом, у вас есть только один синглтон в вашем приложении, остальные - все синглтон-бины в контейнере.

Автор: Don Kirkby Размещён: 24.09.2008 09:08

11 плюса

3978 Репутация автора

Прежде чем реализовать какие-либо другие предложения, задайте себе эти вопросы ...

  • Почему я пытаюсь получить ApplicationContext?
  • Эффективно ли я использую ApplicationContext в качестве локатора службы?
  • Можно ли вообще избежать доступа к ApplicationContext?

Ответы на эти вопросы проще в определенных типах приложений (например, в веб-приложениях), чем в других, но в любом случае их стоит спросить.

Доступ к ApplicationContext нарушает принцип внедрения зависимостей, но иногда у вас нет большого выбора.

Автор: belugabob Размещён: 27.09.2008 08:28

18 плюса

2369 Репутация автора

Я считаю, что вы могли бы использовать 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-компонента из контекста приложения от whereever будет выглядеть примерно так:

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

Команда Spring не рекомендует использовать этот класс и yadayada, но там, где я его использовал, он мне очень подходил.

Автор: stian Размещён: 27.09.2008 09:43

6 плюса

13548 Репутация автора

Если вы используете веб-приложение, существует также другой способ доступа к контексту приложения без использования синглетонов с помощью сервлет-фильтра и ThreadLocal. В фильтре вы можете получить доступ к контексту приложения с помощью WebApplicationContextUtils и сохранить либо контекст приложения, либо необходимые компоненты в TheadLocal.

Внимание: если вы забудете сбросить ThreadLocal, у вас возникнут неприятные проблемы при попытке удалить приложение! Таким образом, вы должны установить его и немедленно начать попытку, которая удаляет ThreadLocal в finally-части.

Конечно, это все еще использует синглтон: ThreadLocal. Но настоящие бобы не должны быть больше. Можно даже определить область запроса, и это решение также работает, если у вас есть несколько WAR-файлов в Приложении с библиотеками в EAR. Тем не менее, вы можете считать это использование ThreadLocal таким же плохим, как и использование простых синглетонов. ;-)

Возможно, Spring уже предлагает подобное решение? Я не нашел, но я не знаю точно.

Автор: Hans-Peter Störr Размещён: 30.03.2009 02:54

113 плюса

6897 Репутация автора

Вы можете реализовать ApplicationContextAwareили просто использовать @Autowired:

public class SpringBean {
  @Autowired
  private ApplicationContext appContext;
}

SpringBeanбудет ApplicationContextвведен, в рамках которого создается этот боб. Например, если у вас есть веб-приложение с довольно стандартной иерархией контекстов:

main application context <- (child) MVC context

и SpringBeanобъявляется в основном контексте, в него вводится основной контекст; в противном случае, если он объявлен в контексте MVC, в него будет вставлен контекст MVC.

Автор: omnomnom Размещён: 14.05.2012 11:31

3 плюса

2387 Репутация автора

Обратите внимание, что, сохраняя любое состояние из текущего 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, создается an ApplicationContext, и любой реализующий bean-компонент ApplicationContextAwareили autowiring ApplicationContextможет записывать в статическую переменную.

Когда выполняется тест B, происходит то же самое, и статическая переменная теперь указывает на тест B ApplicationContext

Когда выполняется тест C, бины не создаются, поскольку TestContext(и в данном случае ApplicationContext) из теста A используется повторно. Теперь у вас есть статическая переменная, указывающая на другую, ApplicationContextотличную от той, которая в данный момент содержит компоненты для вашего теста.

Автор: gogstad Размещён: 20.02.2014 11:52

0 плюса

9048 Репутация автора

Пожалуйста, обратите внимание, что; код ниже создаст новый контекст приложения вместо того, чтобы использовать уже загруженный.

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;
    }
}
Автор: Kanagavelu Sugumar Размещён: 05.12.2016 07:44

5 плюса

181 Репутация автора

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

Автор: Vanessa Schissato Размещён: 21.04.2017 08:16

0 плюса

2558 Репутация автора

Я знаю, что на этот вопрос дан ответ, но я хотел бы поделиться кодом Kotlin, который я сделал, чтобы получить контекст Spring.

Я не специалист, поэтому я открыт для критиков, отзывов и советов:

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, bean-компоненты, созданные вручную классы), как в этом Java-сервлете:

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


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


    public MyWebServlet() {

    }
}
Автор: John John Pichler Размещён: 28.06.2018 12:20

1 плюс

3147 Репутация автора

Существует множество способов получить контекст приложения в приложении 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)метод вы получите приложениеContext

ApplicationContextAware :

Интерфейс, который должен быть реализован любым объектом, который хочет получить уведомление о ApplicationContext, в котором он выполняется. Реализация этого интерфейса имеет смысл, например, когда объект требует доступа к набору взаимодействующих компонентов.

  1. Через Autowired :

    @Autowired
    private ApplicationContext applicationContext;
    

Здесь @Autowiredключевое слово предоставит applicationContext. Autowired имеет некоторые проблемы. Это создаст проблему во время юнит-тестирования.

Спасибо :)

Автор: Md. Sajedul Karim Размещён: 17.10.2018 08:52

1 плюс

10972 Репутация автора

Не уверен, насколько это будет полезно, но вы также можете получить контекст при инициализации приложения. Это самое раннее, что вы можете получить контекст, даже до @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);
Автор: Chloe Размещён: 20.12.2018 08:48

0 плюса

72 Репутация автора

Выполните autowire в Spring bean, как показано ниже: @Autowired private ApplicationContext appContext;

у вас будет объект applicationcontext.

Автор: SandeepJain Размещён: 06.03.2019 05:14
Вопросы из категории :
32x32