Ensure spring bean loaded from non spring context

时间秒杀一切 提交于 2019-11-30 03:27:46

问题


I have spring application alongside (jersey 2.6 classes and ) servlets .

I need to get Spring bean(s) from jersey/non spring context,

Similar question suggested to get context in a static wrapper of context

public static ApplicationContext getContext() {
    return context;
}

How can I be sure the context is already loaded or not null?

If I can't, how should I wait/check until it spring context is loaded?

In case of calling from jersey context or calling bean from a simple HttpServlet code

EDIT

Jersey is working fine using jersey-spring3 dependency jar, so my question is only about Servlets out of Spring control

EDIT 2

The application is loading spring different than @entpnerd suggested article

It register a Servlet implementing a WebApplicationInitializer

public class MyWebAppInitializer implements WebApplicationInitializer {

But also have DispatcherServlet configured in web.xml

How can the DispatcherServlet loaded only after Spring loaded?

Because we add Autowiring capabilities on its init method:

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

Is adding a timeout before serving requests is the most prefer solution or is there a tweak in class loading that can take care of it?

EDIT 3

I found answers and answers of injecting, but not why Spring is loaded before Servlet.


回答1:


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



回答2:


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.
  }
}


来源:https://stackoverflow.com/questions/47434743/ensure-spring-bean-loaded-from-non-spring-context

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