确保从非 spring 上下文加载 spring bean

Ensure spring bean loaded from non spring context

我有 spring 应用程序以及 (jersey 2.6 classes 和 ) servlets .

我需要从 jersey/non spring 上下文中获取 Spring 个 bean,

类似question 建议在上下文的静态包装器中获取上下文

public static ApplicationContext getContext() {
    return context;
}

如何确定上下文是否已加载或不为空?

如果我不能,我应该如何 wait/check 直到它 spring 上下文被加载?

如果从 jersey 上下文调用或从 调用 bean,一个简单的 HttpServlet 代码

编辑

Jersey 使用 jersey-spring3 依赖 jar 工作正常,所以 我的问题只是关于 Servlets 不受 Spring 控制

编辑 2

应用程序正在加载 spring 与 @entpnerd 建议的不同 article

它注册了一个实现 WebApplicationInitializer

的 Servlet
public class MyWebAppInitializer implements WebApplicationInitializer {

但在 web.xml

中也配置了 DispatcherServlet

怎么加载Spring后才加载DispatcherServlet

因为我们在它的 init 方法上添加了自动装配功能:

WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext())
                    .getAutowireCapableBeanFactory().autowireBean(this);

在服务请求之前添加超时是最优选的解决方案,还是 class 加载中的调整可以解决这个问题?

编辑 3

我发现answers and answers注入了,但是没有为什么 Spring在Servlet之前加载

这个想法很简单,尽管实际实施可能会因 Spring 引导和 Jersery 初始化的确切方式而有所不同。

一个想法:

Spring boot 作为一个纯粹的运行时框架,就是正确加载应用程序上下文(从问题的角度来看)。

所以,最重要的是,当它被加载时,内存中某处有一个应用程序上下文,并且可以从该应用程序上下文访问 bean。

现在,由于您说 Jersey 不是 spring/spring-boot 驱动的,因此 Jersey 必须可以从某种静态全局变量访问此应用程序上下文,这很丑陋但应该可以工作。

所以这个想法有两个步骤:

  1. 将应用程序上下文引用放到一些可从 Jersey 访问的静态持有者。
  2. 从 Jersey 组件的一些基础架构级别代码中读取此值。

一个可能的实现

从技术上讲,第一步 可以通过实施某种 spring 启动侦听器来完成,它将应用程序上下文存储在某种单例中:

enum ApplicationContextHolder {
   INSTANCE;
    private ApplicationContext ctx;
    void setApplicationContext(ApplicationContext ctx) {
        this.ctx = ctx;
    }

    ApplicationContext getCtx() {
        return this.ctx;
    }

}


// and a listener (spring boot provides many ways to register one, but the 
// implementation should be something like this):
// The main point is that its managed by spring boot, and hence and access to 
// the application context
class StartupListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {           
             ApplicationContextHolder
             .INSTANCE
             .setApplicationContext(event.getApplicationContext());
    }
}

现在 第 2 步 是:

class MyJerseyOrWhateverComponentThatWantsToAccessApplicationContext {

    public void foo() {
       ApplicationContext ctx = ApplicationContextHolder.INSTANCE.getCtx();
       ... 
       ctx.getBean(...);
    }
}

因此,可行的解决方案可能分两个阶段进行:

  1. A Spring bean 获取 ApplicationContext 实例并将其发送到 Spring 上下文之外的静态单例。
  2. 您的独立 servlet 从静态单例中获取 ApplicationContext 实例并验证是否已加载正确的 bean。

以下面的代码为例:

SpringMetaBean.java

// @Component so that it's part of the Spring context
// Implement ApplicationContextAware so that the ApplicationContext will be loaded
// correctly
@Component
public class SpringMetaBean implements ApplicationContextAware {
  private ApplicationContext appCtx;
  public setApplicationContext(ApplicationContext appCtx) {
    this.appCtx = appCtx;
  }

  // @PostConstruct so that when loaded into the Spring context, this method will
  // automatically execute and notify ApplicationContextHolder with a reference to
  // the ApplicationContext
  @PostConstruct
  public void setup() {
    ApplicationContextHolder.set(this.appCtx);
  }
}

ApplicationContextHolder.java

public class ApplicationContextHolder {
  // ensure the reference is thread-safe because Spring and standalone Servlet will
  // probably be running on different threads.
  private final AtomicReference<ApplicationContext> appCtxContainer = new AtomicReference<>();

  public void set(ApplicationContext appCtx) {
    this.appCtxContainer.set(appCtx);
  }

  public ApplicationContext get() {
    return this.appCtxContainer.get();
  }
}

MyStandaloneServlet.java

public class MyStandaloneServlet {
  // my request handler method
  public void getResponse(HttpServletRequest rq) {
    ApplicationContext springAppCtx = ApplicationContextHolder.get();
    // if not null, we know that Spring has been loaded and we can dig into the
    // application context.
  }
}