Spring scheduled tasks: from XML to annotation - java

In our Spring web-application, we are moving from XML based configuration to Annotation based configuration.
I'm stuck with a scheduled task defined with this XML
<task:scheduled-tasks scheduler="cacheScheduler">
<task:scheduled ref="currencyExchangeRateTask" method="cacheCurrencyExchangeRates" cron="0 0 8,20 * * *" />
</task:scheduled-tasks>
There are multiple schedulers in our web-application. And this task needs to be executed on the scheduler with id cacheScheduler.
I have now the following annotation in place
#Scheduled(cron = "0 0 8,20 * * *")
public void cacheCurrencyExchangeRates() {
...
}
This is executing on the default scheduler.
How can this be fixed without XML configuration?

You can't do it through #Scheduled directly. It doesn't provide any annotation members to specify a bean reference name.
Instead, you have to use SchedulingConfigurer. Define a #Configuration class. Annotate it with #EnableScheduling and #ComponentScan for the packages with component types that have #Scheduled annotated methods. Then have the class implement SchedulingConfigurer.
The ScheduledTaskRegistrar provided through its configureTasks method lets you set a task scheduler.
For example:
#Configuration
#ComponentScan("com.example.tasks")
#EnableScheduling
class Tasks implements SchedulingConfigurer {
#Bean
public TaskScheduler cacheScheduler() {
return new ThreadPoolTaskScheduler();
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setTaskScheduler(cacheScheduler());
}
}
All the #Scheduled methods discovered through this #Configuration class will now be using the TaskScheduler defined within.
If you need different #Scheduled methods to use different TaskScheduler instances, you'll need different #Configuration classes, similarly to needing different <task:scheduled-tasks .../> elements.

Related

#ComponentScan annotation works for non #Configuration classes

Spring reference documentation says the following:
Spring can automatically detect stereotyped classes and register corresponding BeanDefinition instances with the ApplicationContext ...
To autodetect these classes and register the corresponding beans, you need to add #ComponentScan to your #Configuration class ...
I've created a simple example to test auto-detection functionality of Spring framework:
/**
* Java-based configuration class which defines root package to start scanning from.
*/
#ComponentScan
public class ComponentScanPackageMarker {
}
/**
* Class annotated with <b>stereotype</b> annotation is a candidate for automatic detection and registering as
* {#link BeanDefinition} instance.
*/
#Component
public class Pen {
private Ink ink;
#Autowired
public Pen(Ink ink) {
this.ink = ink;
}
}
/**
* Auto-detected class which will be used as auto-wiring candidate for another auto-detected component.
*/
#Component
public class Ink {
}
#Configuration annotation was intentionally omitted for ComponentScanPackageMarker class. I've tested component scanning and autowiring functionality. To my surprise everything went well:
#Test
public void shouldAutoDetectAndRegisterBeans() {
try (AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(ComponentScanPackageMarker.class)) {
Pen pen = context.getBean(Pen.class);
Assert.assertNotNull(pen);
Ink ink = context.getBean(Ink.class);
Assert.assertNotNull(ink);
}
}
Is this behavior of component-scanning intentional? Why does it work even without #Configuration annotation?
Yes.
I think the job of #ComponentScan is to scan given packages and register beans annotated with Stereotype annotations (#Component, #Configuration, #Service, #Repository) if found any while job of #Configuration is to register method's (annotated with #Bean) return value as a bean in the container.
Correct me if I am wrong.

How to combine #Scheduled with Profiles

I've got a class that Spring finds via component scan and that has a method annotated with #Scheduled:
#Component
public class Foo {
...
#Scheduled(fixedDelay = 60000)
public void update() {
...
The value 60000 is ok for production, but in my tests I want it to be 1000.
How can I achieve that? E.g., can I combine #Scheduled with profiles somehow?
Make delay as property:
#Component
public class Foo {
...
#Scheduled(fixedDelay = ${delay})
public void update() {
You may keep 2 property files. For example dev.properties and prod.properties
Spring will load one of it.
<context:property-placeholder
location="classpath:${spring.profiles.active}.properties" />
Create two beans, one for production and one for testing and annotate both with #Profile accordingly like below
#Bean
#Scheduled(fixedDelay = 1000)
#Profile("test")
public void update() {
}
#Bean
#Scheduled(fixedDelay = 60000)
#Profile("dev")
public void update() {
}
In your unit test class you can switch between them by activating the relevant profile like below
#RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from "classpath:/app-config.xml"
#ContextConfiguration("/app-config.xml")
#ActiveProfiles("dev") //or switch to #ActiveProfiles("test") when testing
public class TransferServiceTest {
#Autowired
private TransferService transferService;
#Test
public void testTransferService() {
// test the transferService
}
}
If #ActiveProfiles("dev") is activated only the dev #scheduled bean will be created otherwise test if the test profile is activated.
I solved this issue like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans ... xmlns:task="http://www.springframework.org/schema/task"
... xsi:schemaLocation="... http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.2.xsd ...>
<!-- Everything for "default" profile, including the bean with "#Scheduled(fixedDelay = 60000)" on UpdaterTracker.update() and the "taskScheduler" bean -->
...
<!-- Activate this profile in Arquillian tests -->
<beans profile="arquillian">
<!-- Update more frequently -->
<bean id="updaterTracker" class="com.foo.UpdaterTracker"/>
<task:scheduled-tasks scheduler="taskScheduler">
<task:scheduled ref="updaterTracker" method="update" fixed-delay="1000"/>
</task:scheduled-tasks>
</beans>
</beans>
The first part defines the beans as usual, including an instance of the UpdaterTracker-bean that performs update() every 60 seconds. The last part is only activated in case "arquillian" profile is active, defining another instance of the UpdaterTracker-bean and a scheduled task that executes update() every second.
The solution is not perfect, because it produces 2 instances of UpdaterTracker and 3 scheduled tasks. It could be optimized by directly referencing the first UpdaterTracker instance in so that we get 1 instance and 2 scheduled tasks.
However, this works for me and the solution has advantages: It does not require additional beans to be coded and can cope with multiple profiles.

How to override default AsyncConfigurer in Spring (get rid of "only one configurer may exists")

I must put my own Executor to work with Spring #Async annotation. For this, I wrote the class along the lines
#Configuration
#EnableAsync
public class ConnectedThreads implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
return ...
When I try to run the Spring Boot application with this class, the applications crashes with
Caused by: java.lang.IllegalStateException: Only one AsyncConfigurer may exist
at org.springframework.scheduling.annotation.AbstractAsyncConfiguration.setConfigurers(AbstractAsyncConfiguration.java:68)
There is no another configurer in the project. It is a very small project, and I have full control over it. I myself suspect that the custom configurer may simply conflict with the default configurer.
Is there any possibility to say to Spring that THIS is the configurer I need and it should not look for any other?

Does spring #Scheduled annotated methods runs on different threads?

I have several methods annotated with #Scheduled(fixedDelay=10000).
In the application context, I have this annotation-driven setup:
<task:annotation-driven />
The problem is, sometimes some of the method executions get delayed by seconds and even minutes.
I'm assuming that even if a method takes a while to finish executing, the other methods would still execute. So I don't understand the delay.
Is there a way to maybe lessen or even remove the delay?
For completeness, code below shows the simplest possible way to configure scheduler with java config:
#Configuration
#EnableScheduling
public class SpringConfiguration {
#Bean(destroyMethod = "shutdown")
public Executor taskScheduler() {
return Executors.newScheduledThreadPool(5);
}
...
When more control is desired, a #Configuration class may implement SchedulingConfigurer.
The documentation about scheduling says:
If you do not provide a pool-size attribute, the default thread pool will only have a single thread.
So if you have many scheduled tasks, you should configure the scheduler, as explained in the documentation, to have a pool with more threads, to make sure one long task doesn't delay all the other ones.
If you're using Spring Boot:
There is also a property you can set in your application properties file that increases the pool size:
spring.task.scheduling.pool.size=10
Seems to be there since Spring Boot 2.1.0.
A method annotated with #Scheduled is meant to be run separately, on a different thread at a moment in time.
If you haven't provided a TaskScheduler in your configuration, Spring will use
Executors.newSingleThreadScheduledExecutor();
which returns an ScheduledExecutorService that runs on a single thread. As such, if you have multiple #Scheduled methods, although they are scheduled, they each need to wait for the thread to complete executing the previous task. You might keep getting bigger and bigger delays as the the queue fills up faster than it empties out.
Make sure you configure your scheduling environment with an appropriate amount of threads.
The #EnableScheduling annotation provides the key information and how to resolve it:
By default, will be searching for an associated scheduler definition:
either a unique TaskScheduler bean in the context, or a TaskScheduler
bean named "taskScheduler" otherwise; the same lookup will also be
performed for a ScheduledExecutorService bean. If neither of the two
is resolvable, a local single-threaded default scheduler will be
created and used within the registrar.
When more control is desired, a #Configuration class may implement
SchedulingConfigurer. This allows access to the underlying
ScheduledTaskRegistrar instance. For example, the following example
demonstrates how to customize the Executor used to execute scheduled
tasks:
#Configuration
#EnableScheduling
public class AppConfig implements SchedulingConfigurer {
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
}
#Bean(destroyMethod="shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(100);
}
}
(emphasis added)
you can use:
#Bean()
public ThreadPoolTaskScheduler taskScheduler(){
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(2);
return taskScheduler;
}
Use below link for the reference..great explanation and implementation:
https://crmepham.github.io/spring-boot-multi-thread-scheduling/#:~:text=By%20default%20Spring%20Boot%20will,there%20is%20enough%20threads%20available).
Using XML file add below lines..
<task:scheduler id="taskScheduler" pool-size="15" />
<task:scheduled-tasks scheduler="taskScheduler" >
....
</task:scheduled-tasks>
default spring using a single thread for schedule task. you can using #Configuration for class implements SchedulingConfigurer . referce: https://crmepham.github.io/spring-boot-multi-thread-scheduling/
We need to pass our own thread pool scheduler, otherwise it will use default single threaded executor. Have added below code to fix-
#Bean
public Executor scheduledTaskThreadPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(10);
executor.setThreadNamePrefix("name-");
executor.initialize();
return executor;
}

Using FactoryBean and DisposableBean with Spring's Java Config

The ThreadPoolExecutorFactoryBean is a FactoryBean implementing DisposableBean. When being used in Spring's XML bean definition like this
<bean id="executorService"
class="org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean"/>
the created bean will be an instance of ExecutorService and ensures ThreadPoolExecutorFactoryBean#destroy() is called, once the Spring Application Context is shut down.
Is it possible to configure such a bean with a Spring 3's #Configuration class?
I found this approach the most elegant:
#Configuration
public class Cfg {
public ExecutorService executorService() {
return executorServiceFactoryBean().getObject();
}
#Bean
public ThreadPoolExecutorFactoryBean executorServiceFactoryBean() {
return new ThreadPoolExecutorFactoryBean();
}
}
Notice that executorService() is not annotated with #Bean - but you can still call it from other #Bean-methods requiring ExecutorService. Since ThreadPoolExecutorFactoryBean is annotated with #Bean, Spring will automatically manage its lifecycle (detect DisposableBean, etc.)

Categories