是否有一种方法可以在Spring应用程序中静态/全局地请求ApplicationContext的副本?
假设主类启动并初始化了应用程序上下文,它是否需要通过调用堆栈向下传递给任何需要它的类,或者类是否有一种方法来请求先前创建的上下文?(我假设它必须是单例的?)
是否有一种方法可以在Spring应用程序中静态/全局地请求ApplicationContext的副本?
假设主类启动并初始化了应用程序上下文,它是否需要通过调用堆栈向下传递给任何需要它的类,或者类是否有一种方法来请求先前创建的上下文?(我假设它必须是单例的?)
当前回答
请注意,通过将当前ApplicationContext的任何状态或ApplicationContext本身存储在一个静态变量中(例如使用单例模式),如果使用Spring-test,您将使您的测试不稳定且不可预测。这是因为Spring-test在同一个JVM中缓存和重用应用程序上下文。例如:
测试运行并使用@ContextConfiguration({"classpath:foo.xml"})进行注释。 测试B运行并使用@ContextConfiguration({"classpath:foo.xml", "classpath:bar.xml})进行注释 运行测试C,并且使用@ContextConfiguration({"classpath:foo.xml"})进行注释
当测试A运行时,将创建一个ApplicationContext,并且任何实现ApplicationContextAware或自动装配ApplicationContext的bean都可能写入静态变量。
当测试B运行时,同样的事情也会发生,并且静态变量现在指向测试B的ApplicationContext
当测试C运行时,没有bean被创建,因为测试A的TestContext(这里是ApplicationContext)被重用了。现在,您得到了一个指向另一个ApplicationContext的静态变量,而不是当前为您的测试保存bean的ApplicationContext。
其他回答
在你执行任何其他建议之前,问自己这些问题…
为什么我要获取ApplicationContext? 我是否有效地使用ApplicationContext作为服务定位器? 我可以完全避免访问ApplicationContext吗?
在某些类型的应用程序(例如Web应用程序)中,这些问题的答案比在其他应用程序中更容易,但无论如何都值得一问。
访问ApplicationContext确实违反了整个依赖注入原则,但有时您没有太多选择。
请注意,通过将当前ApplicationContext的任何状态或ApplicationContext本身存储在一个静态变量中(例如使用单例模式),如果使用Spring-test,您将使您的测试不稳定且不可预测。这是因为Spring-test在同一个JVM中缓存和重用应用程序上下文。例如:
测试运行并使用@ContextConfiguration({"classpath:foo.xml"})进行注释。 测试B运行并使用@ContextConfiguration({"classpath:foo.xml", "classpath:bar.xml})进行注释 运行测试C,并且使用@ContextConfiguration({"classpath:foo.xml"})进行注释
当测试A运行时,将创建一个ApplicationContext,并且任何实现ApplicationContextAware或自动装配ApplicationContext的bean都可能写入静态变量。
当测试B运行时,同样的事情也会发生,并且静态变量现在指向测试B的ApplicationContext
当测试C运行时,没有bean被创建,因为测试A的TestContext(这里是ApplicationContext)被重用了。现在,您得到了一个指向另一个ApplicationContext的静态变量,而不是当前为您的测试保存bean的ApplicationContext。
即使在添加了@Autowire之后,如果你的类不是一个RestController或Configuration class, applicationContext对象仍然是空的。尝试用下面创建新类,它工作正常:
@Component
public class SpringContext implements ApplicationContextAware{
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws
BeansException {
this.applicationContext=applicationContext;
}
}
然后,你可以在同一个类中根据你的需要实现一个getter方法,比如通过:
applicationContext.getBean(String serviceName,Interface.Class)
如果需要访问容器的对象是容器中的bean,则只需实现BeanFactoryAware或ApplicationContextAware接口。
如果容器外部的对象需要访问容器,我为spring容器使用了标准的GoF单例模式。这样,您的应用程序中只有一个单例bean,其余的都是容器中的单例bean。
不确定这有多有用,但你也可以在初始化应用程序时获得上下文。这是最快的你可以获得上下文,甚至在@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);