12

I have a Spring Boot application deployed in Tomcat 8. When the application starts I want to start a worker Thread in the background that Spring Autowires with some dependencies. Currently I have this :

@SpringBootApplication
@EnableAutoConfiguration
@ComponentScan
public class MyServer extends SpringBootServletInitializer {   

    public static void main(String[] args) {
        log.info("Starting application");
        ApplicationContext ctx = SpringApplication.run(MyServer.class, args);
        Thread subscriber = new Thread(ctx.getBean(EventSubscriber.class));
        log.info("Starting Subscriber Thread");
        subscriber.start();
    }

In my Docker test environment this works just fine - but when I deploy this to my Linux (Debian Jessie, Java 8) host in Tomcat 8 I never see the "Starting Subscriber Thread" message (and the thread is not started).

Gandalf
  • 9,648
  • 8
  • 53
  • 88
  • So it can run as a standalone server also, without Tomcat. – Gandalf Sep 28 '16 at 02:31
  • 1
    and what is calling this `main` in a `tomcat` environment? – Scary Wombat Sep 28 '16 at 02:31
  • With a method annotated @Bean in MyServer class? – gaston Sep 28 '16 at 02:55
  • 1
    You are aware of the fact that starting a thread in a EE environment is basically a bad thing to do? Just create a service in spring annotate it with `@Scheduled` and let it run on certain intervals. Or even better why do you need this thread? Generally there are better (and more spring integrated) solutions. – M. Deinum Sep 28 '16 at 06:58

3 Answers3

28

The main method is not called when deploying the application to a non-embedded application server. The simplest way to start a thread is to do it from the beans constructor. Also a good idea to clean up the thread when the context is closed, for example:

@Component
class EventSubscriber implements DisposableBean, Runnable {

    private Thread thread;
    private volatile boolean someCondition;

    EventSubscriber(){
        this.thread = new Thread(this);
        this.thread.start();
    }

    @Override
    public void run(){
        while(someCondition){
            doStuff();
        }
    }

    @Override
    public void destroy(){
        someCondition = false;
    }

}
Magnus
  • 7,952
  • 2
  • 26
  • 52
  • 2
    It is not possible to add @Bean on the class level, see https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html – Daniel Dietrich Oct 31 '17 at 16:16
  • Good point, I have changed it to `@Component` instead, although you can register the bean however you want really. – Magnus Nov 01 '17 at 00:08
  • Doing significant work in the constructor, such as starting a thread makes it very hard to test the object. The reply from Snickers3192 should be the accepted answer – xpmatteo Mar 25 '18 at 16:18
  • 1
    @xpmatteo Fair point. If you want to be able to construct the object without starting the thread, you could use a `@PostConstruct` annotation. – Magnus Mar 25 '18 at 22:31
5

You could have a bean which impelements ApplicationListener<ContextRefreshedEvent> It's onApplicationEvent will be called just start your thread there if it hasn't been started already. I think you want the ApplicationReadyEvent by the way.

Edit How to add a hook to the application context initialization event?

@Component
public class FooBar implements ApplicationListener<ContextRefreshedEvent> {

    Thread t = new Thread();

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (!t.isAlive()) {
            t.start();
        }
    }
}
Derrops
  • 7,651
  • 5
  • 30
  • 60
  • 1
    I know SO frowns upon examples in links rather than answers, but [here's spring's guide to using ApplicationListnener](https://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2) – Gab Jul 24 '18 at 16:50
  • There you go @KCK – Derrops Feb 08 '19 at 05:12
0

Starting a Thread manually is probably not the best way to do it. Instead, use Springs Task execution and Scheduling functionionality.

Kees van Dieren
  • 1,232
  • 11
  • 15