首页 文章

获取Spring应用程序上下文

提问于
浏览
195

有没有办法在Spring应用程序中静态/全局请求ApplicationContext的副本?

假设主类启动并初始化应用程序上下文,是否需要将它通过调用堆栈传递给任何需要它的类,或者有没有办法让类询问先前创建的上下文? (我认为必须是单身?)

12 回答

  • 0

    如果需要访问容器的对象是容器中的bean,则只需实现BeanFactoryAwareApplicationContextAware接口 .

    如果容器外的对象需要访问容器,我使用standard GoF singleton pattern作为 spring 容器 . 这样,您的应用程序中只有一个单例,其余的都是容器中的单例bean .

  • 11

    您可以实现 ApplicationContextAware 或只使用 @Autowired

    public class SpringBean {
      @Autowired
      private ApplicationContext appContext;
    }
    

    SpringBean 将注入 ApplicationContext ,在此实例化此bean . 例如,如果您的Web应用程序具有非常标准的上下文层次结构:

    main application context <- (child) MVC context
    

    并且 SpringBean 在主上下文中声明,它将注入主上下文;否则,如果它在MVC上下文中声明,它将注入MVC上下文 .

  • 4

    这是一个不错的方式(不是我的,原始参考在这里:http://sujitpal.blogspot.com/2007/03/accessing-spring-beans-from-legacy-code.html

    我用过这种方法,它运行正常 . 基本上它是一个简单的bean,它包含对应用程序上下文的(静态)引用 . 通过在spring配置中引用它,它已初始化 .

    看看原始参考,非常清楚 .

  • 3

    我相信你可以使用SingletonBeanFactoryLocator . beanRefFactory.xml文件将保存实际的applicationContext,它将是这样的:

    <bean id="mainContext" class="org.springframework.context.support.ClassPathXmlApplicationContext">
         <constructor-arg>
            <list>
                <value>../applicationContext.xml</value>
            </list>
         </constructor-arg>
     </bean>
    

    从wheverver获取来自applicationcontext的bean的代码将是这样的:

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

    Spring团队不鼓励使用这个类和yadayada,但它在我使用它的地方很适合我 .

  • 37

    在您实施任何其他建议之前,请问自己这些问题......

    • 为什么我要尝试获取ApplicationContext?

    • 我是否有效地使用ApplicationContext作为服务定位器?

    • 我可以完全避免访问ApplicationContext吗?

    这些问题的答案在某些类型的应用程序(例如Web应用程序)中比在其他应用程序中更容易,但无论如何都值得提出 .

    访问ApplicationContext确实违反了整个依赖注入原则,但有时你没有太多选择 .

  • 6

    如果您使用Web应用程序,还可以通过使用servletfilter和ThreadLocal来访问应用程序上下文而不使用单例 . 在过滤器中,您可以使用WebApplicationContextUtils访问应用程序上下文,并在TheadLocal中存储应用程序上下文或所需的bean .

    警告:如果您忘记取消设置ThreadLocal,则在尝试取消部署应用程序时会遇到令人讨厌的问题!因此,您应该设置它并立即启动尝试以取消最终部分中的ThreadLocal .

    当然,这仍然使用单例:ThreadLocal . 但实际的bean不再需要了 . 甚至可以是请求范围的,如果在具有EAR中的库的应用程序中有多个WAR,则此解决方案也可以工作 . 不过,您可能会认为ThreadLocal的使用与使用普通单例一样糟糕 . ;-)

    也许Spring已经提供了类似的解决方案?我找不到一个,但我不确定 .

  • 1

    看看ContextSingletonBeanFactoryLocator . 它提供静态访问器来获取Spring的上下文,假设它们已经以某种方式注册 .

    它不漂亮,比你想要的更复杂,但它有效 .

  • 18
    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

  • 0

    请注意,通过将当前 ApplicationContextApplicationContext 本身中的任何状态存储在静态变量中(例如使用单例模式),如果使用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 或自动装配的bean ApplicationContext 可能会写入静态变量 .

    当测试B运行同样的事情发生时,静态变量现在指向测试B的 ApplicationContext

    当测试C运行时,不会创建任何bean,因为来自测试A的 TestContext (此处为 ApplicationContext )被重用 . 现在你有一个静态变量指向另一个 ApplicationContext 而不是当前持有测试bean的那个 .

  • 164

    在Spring应用程序中有很多方法可以获取应用程序上下文 . 这些是下面给出的:

    • Via 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时,实现此接口是有意义的 .

    • Via Autowired
    @Autowired
    private ApplicationContext applicationContext;
    

    这里 @Autowired 关键字将提供applicationContext . 自动装配有一些问题 . 它会在单元测试期间产生问题 .

    谢谢 :)

  • 113

    请注意;下面的代码将创建新的应用程序上下文,而不是使用已经加载的上下文 .

    private static final ApplicationContext context = 
                   new ClassPathXmlApplicationContext("beans.xml");
    

    另请注意, beans.xml 应该是 src/main/resources 的一部分,在战争中它是 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 路径是difficult . ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml") 无法找到该文件 .

    因此,最好使用注释来使用现有的applicationContext .

    @Component
    public class OperatorRequestHandlerFactory {
    
        public static ApplicationContext context;
    
        @Autowired
        public void setApplicationContext(ApplicationContext applicationContext) {
            context = applicationContext;
        }
    }
    
  • 4

    我知道这个问题已得到解答,但我想分享我用来检索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() {
    
        }
    }
    

相关问题