获取Spring应用上下文

245

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

假设主类启动并初始化应用程序上下文,是否需要将其通过调用堆栈传递到需要它的任何类中,还是有一种方法可供类请求先前创建的上下文? (我认为必须是单例模式?)

17个回答

3

不确定这会有多大用处,但您在初始化应用程序时也可以获得上下文。 这是您可以获得上下文的最早时间,甚至早于@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);

1

我使用一种简单标准的方法来允许外部访问我的任何单例Spring Bean。通过这种方法,我继续让Spring实例化Bean。 我的做法如下:

  1. 定义一个与封闭类相同类型的私有静态变量。
  2. 在类的每个构造函数中将该变量设置为this。如果该类没有构造函数,则添加一个默认构造函数以设置该变量。
  3. 定义一个公共静态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环境。

这对我来说是一个很严重的代码异味;你为了非Spring遗留代码的好处而摧残了所有的bean。上面的ApplicationContextAware单例有一个优点,就是将遗留映射隔离到一个单一的点上,一旦迁移完成(经过X年之后..),它可以很容易地被移除掉。 - undefined

1
在Spring bean中进行自动装配,如下所示:

Do autowire in Spring bean as below:

@Autowired
private ApplicationContext appContext;

您将获得ApplicationContext对象。

0

方法一:您可以通过实现ApplicationContextAware接口来注入ApplicationContext。参考link

@Component
public class ApplicationContextProvider implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

方法二:在任何由Spring管理的bean中自动装配应用程序上下文。

@Component
public class SpringBean {
  @Autowired
  private ApplicationContext appContext;
}

参考 链接


-1

我知道这个问题已经有答案了,但我想分享一下我编写的用于检索Spring上下文的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() {

    }
}

-1
请注意:下面的代码将创建新的应用程序上下文,而不是使用已加载的上下文。
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路径是困难的ClassPathXmlApplicationContext(“META-INF / spring / applicationContext.xml”)将无法定位该文件。

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

@Component
public class OperatorRequestHandlerFactory {

    public static ApplicationContext context;

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        context = applicationContext;
    }
}

-1
即使添加了@Autowire,如果您的类不是RestController或Configuration类,则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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接