是否有一种方法可以在Spring应用程序中静态/全局地请求ApplicationContext的副本?
假设主类启动并初始化了应用程序上下文,它是否需要通过调用堆栈向下传递给任何需要它的类,或者类是否有一种方法来请求先前创建的上下文?(我假设它必须是单例的?)
是否有一种方法可以在Spring应用程序中静态/全局地请求ApplicationContext的副本?
假设主类启动并初始化了应用程序上下文,它是否需要通过调用堆栈向下传递给任何需要它的类,或者类是否有一种方法来请求先前创建的上下文?(我假设它必须是单例的?)
当前回答
我知道这个问题已经有了答案,但是我想分享一下我用来检索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上下文是公开可用的,能够独立于上下文调用相同的方法(junit测试,bean,手动实例化类),就像下面这个Java Servlet:
@WebServlet(name = "MyWebHook", value = "/WebHook")
public class MyWebServlet extends HttpServlet {
private MyBean byBean
= SpringUtils.INSTANCE.ctx(this, true).getBean(MyBean.class);
public MyWebServlet() {
}
}
其他回答
这里有一个很好的方法(不是我的,原始参考在这里: http://sujitpal.blogspot.com/2007/03/accessing-spring-beans-from-legacy-code.html
我使用过这种方法,效果很好。基本上,它是一个简单的bean,包含对应用程序上下文的(静态)引用。通过在spring配置中引用它来初始化它。
看看原来的裁判,非常清楚。
我相信你可以使用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,但它很适合我使用它的地方。
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
我知道这个问题已经有了答案,但是我想分享一下我用来检索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上下文是公开可用的,能够独立于上下文调用相同的方法(junit测试,bean,手动实例化类),就像下面这个Java Servlet:
@WebServlet(name = "MyWebHook", value = "/WebHook")
public class MyWebServlet extends HttpServlet {
private MyBean byBean
= SpringUtils.INSTANCE.ctx(this, true).getBean(MyBean.class);
public MyWebServlet() {
}
}
我使用一种简单、标准化的方法允许外部访问我自己的任何单例Spring bean。使用这个方法,我继续让Spring实例化Bean。我是这么做的:
定义与外围类类型相同的私有静态变量。 在类的每个构造函数中将该变量设置为this。如果类没有构造函数,则添加一个用于设置变量的默认构造函数。 定义一个返回单例变量的公共静态getter方法。
这里有一个例子:
@Component
public class MyBean {
...
private static MyBean singleton = null;
public MyBean() {
...
singleton = this;
}
...
public void someMethod() {
...
}
...
public static MyBean get() {
return singleton;
}
}
然后我可以在单例bean上调用someMethod,在我代码中的任何地方,通过:
MyBean.get().someMethod();
如果您已经子类化了您的ApplicationContext,您可以直接将这种机制添加到它中。否则,您可以子类化它来完成这个任务,或者将这个机制添加到任何可以访问ApplicationContext的bean中,然后使用它来从任何地方访问ApplicationContext。重要的是,正是这个机制让您能够进入Spring环境。