1

I have a Spring @Configuration class as follows:

@Configuration
public class ExecutorServiceConfiguration {

    @Bean
    public BlockingQueue<Runnable> queue() {
        return ArrayBlockingQueue<>(1000);
    }     

    @Bean
    public ExecutorService executor(BlockingQueue<Runnable> queue) {
        return ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, queue);
    }

    @PreDestroy
    public void shutdownExecutor() {
        // no executor instance
    }
}

I would also like to specify a @PreDestroy method which shuts down the ExecutorService. However, the @PreDestroy method cannot have any arguments which is why I'm not able to pass the executor bean to this method in order to shut it. Specifying destroy method in @Bean(destroyMethod = "...") does not work either. It allows me to specify existing shutdown or shutdownNow, but not the custom method which I intend to use.

I know I could instantiate the queue and executor directly and not as Spring beans, but I'd rather do it this way.

zenn1337
  • 481
  • 1
  • 5
  • 25
  • Instead of returning the ThreadPoolExecutor directly, add it to a container then return it and use the container in your shutdownExecutor method? – karen Sep 19 '18 at 09:18
  • you could separate creation and management of the executor and autowire it to the service, or you could assign it to a private member in this class thereby making it visible to `shutdownExecutor()`. You could also ask `ApplicationContext.getBean()` but getting beans out of context is considered an anti-pattern. – diginoise Sep 19 '18 at 09:23
  • I thought about assigning it to the private variable, but I found it somehow ugly. Shutting it down in other component is an option. – zenn1337 Sep 19 '18 at 09:26
  • 1
    @zenn1337 I don't find a private variable ugly at all provided the executor service is actually a singleton. – gpeche Sep 19 '18 at 09:46

2 Answers2

4

I love defining classes inline:

@Bean(destroyMethod = "myCustomShutdown")
public ExecutorService executor(BlockingQueue<Runnable> queue) {
    return new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, queue) {
        public void myCustomShutdown() {
            ...
        }
    };
}
gpeche
  • 21,974
  • 5
  • 38
  • 51
2

Use the ThreadPoolTaskExecutor which does all that by default.

@Configuration
public class ExecutorServiceConfiguration {

    @Bean
    public ThreadPoolTaskExecutor executor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor() {
            protected BlockingQueue<Runnable> createQueue(int queueCapacity) {
                return new ArrayBlockingQueue<>(queueCapacity);
            }
        };
        taskExecutor.setCorePoolSize(1);
        taskExecutor.setMaxPoolSize(1);
        taskExecutor.setKeepAliveSeconds(0);
        taskExecutor.setQueueCapacity(1000);
        return taskExecutor;
    }    
}

This will configure the ThreadPoolExecutor and shutdown when the application stops.

If you don't need the ArrayBlockingQueue but can live with the default LinkedBlockingQueue and only need to specify the queue capacity you can remove the override createQueue method.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
  • Thank you for your answer, it's probably the best I could do. I'm however accepting gpeche's answer since it directly resolves the `@PreDestroy` issue. I do need to have the queue as a separate bean, cause I have to be able to clear all queued unprocessed tasks if some event occurs. – zenn1337 Sep 19 '18 at 10:42