Abhishek Modi
Abhishek Modi

Reputation: 187

Injecting a bean at startup in Spring

I have a class RunBackgroundServices which runs some background services at startup. I need a new object BackgroundServices in it. So I am using WebApplicationContext to get the bean. But it's not working.

RunBackgroundServices.java

 @WebListener
public class RunBackgroundServices implements ServletContextListener {

    private ExecutorService executor;

    public void contextInitialized(ServletContextEvent event) {
        final WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(event.getServletContext());
        BackgroundServices backgroundServices = springContext.getBean(BackgroundServices.class);

        executor = Executors.newSingleThreadExecutor();
        executor.submit(backgroundServices); // Task should implement Runnable.
    }

    public void contextDestroyed(ServletContextEvent event) {
        executor.shutdown();
    }

}

BackgroundServices.java

@Service
public class BackgroundServices extends Thread {

    @Autowired
    ServerListener serverListener;

    @Autowired
    ClientListener clientListener;

    private static final Logger logger = LoggerFactory
            .getLogger(BackgroundServices.class);

    public void run() {
        logger.debug("BackgroundServices :: run");
        try {
            serverListener.start();
        } catch (InterruptedException e) {
            logger.error(e.getStackTrace().toString());
        }

        try { 
            clientListener.start();
        } catch (InterruptedException e) {
            logger.error(e.getStackTrace().toString());
        }

    }
}

I am getting the following error -

Exception sending context initialized event to listener instance of class com.emc.hl7.common.RunBackgroundServices
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.emc.hl7.common.BackgroundServices] is defined
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:295)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1125)
    at com.emc.hl7.common.RunBackgroundServices.contextInitialized(RunBackgroundServices.java:20)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4961)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5455)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:634)
    at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:671)
    at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1840)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

Upvotes: 0

Views: 1371

Answers (1)

geoand
geoand

Reputation: 64079

There is an easier way to perform bootstraping operations using Spring. All you have to do is have Spring bean that implements ApplicationListener<ContextRefreshedEvent>

Your code would look like:

@Component
public class ContextStartupListener implements ApplicationListener<ContextRefreshedEvent> {

    private final BackgroundServices backgroundServices;

    private ExecutorService executor;

    @Autowired
    public ContextStartupListener(BackgroundServices backgroundServices) {
        this.backgroundServices= backgroundServices;
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if(!isRootApplicationContext(event)) {
            return;
        }

        executor = Executors.newSingleThreadExecutor();
        executor.submit(backgroundServices);
    }

    private boolean isRootApplicationContext(ContextRefreshedEvent event) {
        return null == event.getApplicationContext().getParent();
    }
}

Note the use of isRootApplicationContext. It is needed if you have multiple application contexts and do no want to run the bootstrapping operation on each one.

Using the above code you are bootstrapping using Spring's events, not the Servlet container's events.

Upvotes: 5

Related Questions