Получение Spring Контекст приложения
Есть ли способ статически/глобально запросить копию ApplicationContext в приложении Spring?
Предполагая, что основной класс запускает и инициализирует контекст приложения, нужно ли передавать это через стек вызовов в любые классы, которые в нем нуждаются, или есть способ, чтобы класс запрашивал ранее созданный контекст? (Что я предполагаю, должен быть одиночным?)
Ответы
Ответ 1
Если объект, который нуждается в доступе к контейнеру, является bean в контейнере, просто выполните BeanFactoryAware или ApplicationContextAware.
Если для объекта, находящегося за пределами контейнера, нужен доступ к контейнеру, я использовал стандартный шаблон OneF для GoF для контейнера spring. Таким образом, у вас есть только один синглтон в вашем приложении, остальные - одноточие beans в контейнере.
Ответ 2
Вы можете реализовать ApplicationContextAware
или просто использовать @Autowired
:
public class SpringBean {
@Autowired
private ApplicationContext appContext;
}
SpringBean
будет введено ApplicationContext
, внутри которого будет создан этот bean. Например, если у вас есть веб-приложение с довольно стандартной иерархией контекстов:
main application context <- (child) MVC context
и SpringBean
объявляется в основном контексте, в него будет вложен основной контекст;
в противном случае, если он объявлен в контексте MVC, он будет иметь контекст MVC.
Ответ 3
Здесь хороший способ (не мой, оригинальная ссылка здесь:
http://sujitpal.blogspot.com/2007/03/accessing-spring-beans-from-legacy-code.html
Я использовал этот подход, и он отлично работает. В основном это простой bean, который содержит (статическую) ссылку на контекст приложения. Отправляя его в конфигурацию spring, он инициализируется.
Взгляните на оригинал ref, это очень ясно.
Ответ 4
Я считаю, что вы можете использовать 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 из контекста приложения frombodyree, будет примерно таким:
BeanFactoryLocator bfl = SingletonBeanFactoryLocator.getInstance();
BeanFactoryReference bf = bfl.useBeanFactory("mainContext");
SomeService someService = (SomeService) bf.getFactory().getBean("someService");
Команда Spring препятствует использованию этого класса и yadayada, но мне это хорошо подходит, где я использовал его.
Ответ 5
Прежде чем выполнять какие-либо другие предложения, задайте себе эти вопросы...
- Почему я пытаюсь получить ApplicationContext?
- Я эффективно использую ApplicationContext в качестве локатора службы?
- Можно ли вообще избежать доступа к ApplicationContext?
Ответы на эти вопросы проще в некоторых типах приложений (например, в веб-приложениях), чем в других, но их все равно стоит спросить.
Доступ к ApplicationContext действительно нарушает весь принцип инъекции зависимостей, но иногда у вас нет выбора.
Ответ 6
Если вы используете веб-приложение, есть еще один способ получить доступ к контексту приложения, не используя синглтоны, используя сервлетфильтр и ThreadLocal. В фильтре вы можете получить доступ к контексту приложения с помощью WebApplicationContextUtils и сохранить либо контекст приложения, либо необходимый beans в TheadLocal.
Предостережение: если вы забудете отменить ThreadLocal, вы столкнетесь с неприятными проблемами при попытке развернуть приложение! Таким образом, вы должны установить его и сразу же запустить попытку, которая отключает ThreadLocal в последней части.
Конечно, это все еще использует singleton: ThreadLocal. Но фактический beans больше не нужен. Это может быть даже область запроса, и это решение также работает, если у вас есть несколько WARs в приложении с библиотеками в EAR. Тем не менее, вы можете рассматривать это использование ThreadLocal так же плохо, как использование простых синглетонов.; -)
Возможно, Spring уже предлагает аналогичное решение? Я не нашел его, но я точно не знаю.
Ответ 7
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
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
Ответ 8
Взгляните на ContextSingletonBeanFactoryLocator. Он предоставляет статическим аксессуарам, чтобы овладеть контекстами Spring, если они были зарегистрированы определенными способами.
Это не красиво и сложнее, чем вам хотелось бы, но он работает.
Ответ 9
Обратите внимание, что, сохраняя любое состояние из текущего ApplicationContext
или самого ApplicationContext
в статической переменной, например, используя шаблон singleton, вы сделаете ваши тесты неустойчивыми и непредсказуемыми, если вы используете Spring -контрольная работа. Это связано с тем, что Spring -test кэширует и повторно использует контексты приложений в одной JVM. Например:
- Тестирование Запуск и аннотация с помощью
@ContextConfiguration({"classpath:foo.xml"})
.
- Тест B запускается и аннотируется с помощью
@ContextConfiguration({"classpath:foo.xml", "classpath:bar.xml})
- Тест C запускается и аннотируется с помощью
@ContextConfiguration({"classpath:foo.xml"})
Когда выполняется тест A, создается ApplicationContext
, и любая статическая переменная beans ApplicationContextAware
или autowiring ApplicationContext
может записывать статическую переменную.
Когда выполняется тест B, то статическая переменная теперь указывает на тест B ApplicationContext
При запуске Test C не создается beans в качестве TestContext
(и здесь ApplicationContext
) из теста A. Теперь у вас есть статическая переменная, указывающая на другую ApplicationContext
, чем та, которая в настоящее время содержит beans для вашего теста.
Ответ 10
Существует множество способов получить контекст приложения в приложении Spring. Те приведены ниже:
-
Через 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-компонентов.
-
Через Autowired:
@Autowired
private ApplicationContext applicationContext;
Здесь @Autowired
слово @Autowired
предоставит applicationContext. Autowired имеет некоторые проблемы. Это создаст проблему во время юнит-тестирования.
Ответ 11
Не уверен, насколько это будет полезно, но вы также можете получить контекст при инициализации приложения. Это самый @Autowire
получить контекст, даже до @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);
Ответ 12
Обратите внимание, что; ниже код создаст новый контекст приложения вместо использования уже загруженного.
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;
}
}
Ответ 13
Я знаю, что на этот вопрос дан ответ, но я хотел бы поделиться кодом 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() {
}
}
Ответ 14
Выполните autowire в Spring bean, как показано ниже: @Autowired private ApplicationContext appContext;
вы будете объектом applicationcontext.