Ensure spring bean loaded from non spring context

纵饮孤独 提交于 2019-11-30 19:22:40

The idea is quite simple, although the actual implementation may vary depending on an exact way of Spring boot and Jersery initialization.

An idea:

Spring boot, as being a purely runtime framework, is all about proper loading the application context (from the question standpoint).

So, bottom line, when it's loaded there is an application context somewhere in memory, and its possible to access beans from this application context.

Now, since you say that Jersey is not spring/spring-boot driven, this application context has to be reachable from some kind of static global variable by Jersey, it's quite ugly but should work.

So the idea has two steps:

  1. Put an application context reference to some static holder accessible from Jersey.
  2. Read this value in some infrastructure level code from Jersey component.

A Possible Implementation

Technically step one can be done by implementing some kind of spring boot listener that will store application context in some kind of singleton:

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());
    }
}

Now the step 2 is:

class MyJerseyOrWhateverComponentThatWantsToAccessApplicationContext {

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

So a viable solution for this could happen in two stages:

  1. A Spring bean gets the ApplicationContext instance and sends it to a static singleton outside of the Spring context.
  2. Your standalone servlet gets the ApplicationContext instance from the static singleton and verifies that the right beans have been loaded.

Consider the following code as an example:

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.
  }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!