获得Spring应用程序上下文
有没有办法在Spring应用程序中静态/全局地请求一个ApplicationContext的副本?
假设主类启动并初始化应用程序上下文,它是否需要通过调用堆栈传递给需要它的任何类,或者是否有类请求以前创build的上下文? (我认为这是一个单身?)
如果需要访问容器的对象是容器中的bean,只需实现BeanFactoryAware或ApplicationContextAware接口。
如果容器外部的对象需要访问容器,我已经使用了一个标准的GoF单例模式作为spring容器。 这样,你的应用程序中只有一个单例,其余的都是容器中的单例bean。
你可以实现ApplicationContextAware
或者只使用@Autowired
:
public class SpringBean { @Autowired private ApplicationContext appContext; }
SpringBean
将注入ApplicationContext
,在这个bean中实例化这个bean。 例如,如果你有一个漂亮的标准上下文层次结构的Web应用程序:
main application context <- (child) MVC context
SpringBean
是在主要的上下文中声明的,它会注入主要的上下文; 否则,如果在MVC上下文中声明它,则会注入MVC上下文。
这是一个很好的方式(不是我的,原始的参考在这里: http : //sujitpal.blogspot.com/2007/03/accessing-spring-beans-from-legacy-code.html
我用这种方法,它工作正常。 基本上它是一个简单的bean,它拥有对应用程序上下文的(静态)引用。 通过在springconfiguration中引用它进行初始化。
看看原来的参考,这是非常清楚的。
我相信你可以使用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,但是它适合我使用它的地方。
在实施其他任何build议之前,问问自己这些问题…
- 为什么我想要获得ApplicationContext?
- 我是否有效地使用ApplicationContext作为服务定位器?
- 我可以避免访问ApplicationContext吗?
在某些types的应用程序(例如Web应用程序)中,这些问题的答案比在其他types的应用程序中更容易,但无论如何都值得问。
访问ApplicationContext确实违反了整个dependency injection的原则,但有时你没有太多的select。
如果使用Web应用程序,还有另一种方法来访问应用程序上下文,而不使用servletfilter和ThreadLocal使用单例。 在filter中,您可以使用WebApplicationContextUtils访问应用程序上下文,并将应用程序上下文或所需的Bean存储在TheadLocal中。
警告:如果忘记取消设置ThreadLocal,则在尝试取消部署应用程序时会遇到令人讨厌的问题! 因此,你应该设置它,并立即开始一个尝试,在最后部分中取消设置ThreadLocal。
当然,这仍然使用单例:ThreadLocal。 但是实际的bean不需要了。 甚至可以是请求范围,如果在EAR中有libaries的应用程序中有多个WAR,这个解决scheme也可以工作。 不过,你可能会认为这种使用ThreadLocal与使用普通单例一样糟糕。 😉
也许Spring已经提供了类似的解决scheme? 我没有find,但我不知道。
看看ContextSingletonBeanFactoryLocator 。 它提供静态访问器来获取Spring的上下文,假设它们已经以某种方式注册了。
这不是很漂亮,也许比你想要的更复杂,但它的工作原理。
请注意,通过将当前ApplicationContext
任何状态或ApplicationContext
本身存储在一个静态variables中(例如使用单例模式),如果使用Spring-test,则会使testing变得不稳定和不可预知。 这是因为Spring-test在同一个JVM中caching和重用了应用程序上下文。 例如:
- testing一个运行,它用
@ContextConfiguration({"classpath:foo.xml"})
注释。 - testingB运行并使用
@ContextConfiguration({"classpath:foo.xml", "classpath:bar.xml})
- testingC运行,它用
@ContextConfiguration({"classpath:foo.xml"})
注释
当运行testingA时,将创build一个ApplicationContext
,任何执行ApplicationContextAware
或自动assemblyApplicationContext
Bean都可能写入该静态variables。
当testingB运行相同的事情时,静态variables现在指向testingB的ApplicationContext
当Test C运行时, 不会创buildBean,因为Test A的TestContext
(以及ApplicationContext
)将被重用。 现在你得到一个指向另一个ApplicationContext
的静态variables,而不是那个当前持有你的testingbean的那个variables。
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
请注意, 下面的代码将创build新的应用程序上下文,而不是使用已经加载的。
private static final ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
另外请注意, beans.xml
应该是src/main/resources
中的一部分,在war中它是WEB_INF/classes
一部分,其中真正的应用程序将通过Web.xml
提到的applicationContext.xml加载。
<context-param> <param-name>contextConfigLocation</param-name> <param-value>META-INF/spring/applicationContext.xml</param-value> </context-param>
在ClassPathXmlApplicationContext
构造函数中很难提及applicationContext.xml
path。 ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml")
将无法find该文件。
所以最好通过使用注释来使用现有的applicationContext。
@Component public class OperatorRequestHandlerFactory { public static ApplicationContext context; @Autowired public void setApplicationContext(ApplicationContext applicationContext) { context = applicationContext; } }
- 使用@PropertySource批注时,@Value未parsing。 如何configurationPropertySourcesPlaceholderConfigurer?
- Spring 3.1 entityManagerFactory java.lang.NoSuchFieldError:NULL错误
- 春季启动:是否有可能使用外部application.properties文件在任何目录与胖胖的jar?
- 解释为什么构造函数注入比其他选项更好
- 什么是Spring中的Dispatcher Servlet?
- Spring MVC控制器是单身吗?
- 无法打开ServletContext资源
- 我应该为业务层使用EJB3还是Spring?
- 弹簧启动默认H2 jdbc连接(和H2控制台)