简体   繁体   中英

Ordering Listeners in Spring's WebApplicationInitializer

I have a custom ServletContextListener that I am using to initialize and start up a Cron4J scheduler.

public class MainListener implements ServletContextListener {
    @Value("${cron.pattern}")
    private String dealHandlerPattern;

    @Autowired
    private DealMoqHandler dealMoqHandler;
}

I am autowiring some objects in the Listener as shown, and would like for Spring to manage the listener's instantiation. I am using programmatic web.xml configuration through WebApplicationInitializer , but so far the Listener isn't being autowired (NullPointerExceptions whenever I try to access the supposedly autowired objects).

I've already tried to add my customer listener after adding the ContextLoaderListener, shown below:

public class CouponsWebAppInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext container) throws ServletException {
        AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
        rootContext.register(SpringAppConfig.class);

        // Manage the lifecycle of the root application context
        container.addListener(new ContextLoaderListener(rootContext));

        container.addListener(new MainListener()); //TODO Not working
    }

I checked these past questions Spring - Injecting a dependency into a ServletContextListener and dependency inject servlet listener and tried to implement the following code inside the contextInitialized method of my listener:

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

However, I just get the following exception:

Exception sending context initialized event to listener instance of class com.enovax.coupons.spring.CouponsMainListener: java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered?
at org.springframework.web.context.support.WebApplicationContextUtils.getRequiredWebApplicationContext(WebApplicationContextUtils.java:90) [spring-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]

How can I make sure that Spring has already finished instantiation before adding my listener?

My mistake. It turns out the code in the original post is correct, and the listeners are being added in the correct order.

The issue was that I had annotated my custom listener with a @WebListener annotation (Servlet 3.0). This was causing the web app to disregard my addListener() code in WebApplicationInitializer, and instantiate the custom listener AHEAD of Spring's ContextLoaderListener.

The following code block illustrates the ERRONEOUS code:

@WebListener /* should not have been added */
public class CouponsMainListener implements ServletContextListener {
    @Autowired
    private Prop someProp;
}

You cannot use new with Spring beans - Java doesn't care about Spring and Spring has no way to modify the behavior of the new operator. If you create your objects yourself, you need to wire them yourself.

You also need to be careful what you do during initialization. When using Spring, use this (simplified) model: First, Spring creates all the beans (calls new for all of them). Then it starts to wire them.

So you must be extra careful when you start to use the autowired fields. You can't always use them right away, you need to make sure that Spring is finished initializing everything.

In some cases, you can't even use autowired fields in @PostProcess methods because Spring couldn't create the bean because of cyclic dependencies.

So my guess is that "whenever I try to access" is too early.

For the same reason, you can't use WebApplicationContextUtils in the WebApplicationInitializer : It hasn't finished setting up Spring, yet.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM