Enable Scheduling
Initialize a spring new project and add awaitility library.
<!-- Spring Boot Core -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- Awaitility for async tests -->
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.2.0</version>
<scope>test</scope>
</dependency>
Example 1
Enable scheduling in you App with @EnableScheduling annotation.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling // Look Here
public class TimeApp {
public static void main(String[] args) {
SpringApplication.run(TimeApp.class, args);
}
}
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
public class TimeComponent {
private static final SimpleDateFormat dateFormat =
new SimpleDateFormat("HH:mm:ss");
@Scheduled(fixedRate = 1000) // 1 sec
public void runningTime() {
System.out.println(
"Time is: " + dateFormat.format(new Date())
);
}
}
mvn spring-boot:run
# Time is: 17:36:24
# Time is: 17:36:25
# Time is: 17:36:26
Example 2
Schedule a task with Spring Boot and test it using Awaitility.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class CounterApp {
public static void main(String[] args) {
SpringApplication.run(CounterApp.class, args);
}
}
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Service
public class CounterService {
private final AtomicInteger counter = new AtomicInteger(0);
@Scheduled(fixedRate = 1000) // runs every 1 second
public void increaseCounter() {
counter.incrementAndGet();
System.out.println("Counter increment: " + getCounter());
}
public int getCounter() {
return counter.get();
}
}
mvn spring-boot:run
# Counter incremented: 1
# Counter incremented: 2
# Counter incremented: 3
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.awaitility.Awaitility.await;
import java.time.Duration;
@SpringBootTest
public class CounterServiceTest {
@Autowired
private CounterService service;
@Test
void counterShouldIncreaseAutomatically() {
// Wait up to 5 seconds until counter >= 2
await()
.atMost(Duration.ofSeconds(5))
.until(() -> service.getCounter() >= 2);
}
}
mvn test
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
Example 3
This simulates calling an external market-data API, a random price is used instead of real HTTP calls.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class FxApp {
public static void main(String[] args) {
SpringApplication.run(FxApp.class, args);
}
}
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.concurrent.atomic.AtomicReference;
@Service
public class FxRateService {
private final AtomicReference<Double> latestRate = new AtomicReference<>();
// Simulate external API call
private double fetchFxRateFromProvider() {
return 4.5 + Math.random(); // pretend EUR/RON fluctuates
}
@Scheduled(fixedRate = 5000) // every 5 seconds
public void updateRate() {
double newRate = fetchFxRateFromProvider();
latestRate.set(newRate);
System.out.println("FX updated: " + newRate);
}
public Double getLatestRate() {
return latestRate.get();
}
}
mvn spring-boot:run
FX updated: 5.216310420189029
FX updated: 5.298944643788635
FX updated: 5.039270121742039
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.awaitility.Awaitility.await;
import java.time.Duration;
@SpringBootTest
public class FxRateServiceTest {
@Autowired
FxRateService fxRateService;
@Test
void fxRateShouldBeUpdatedByScheduler() {
await()
.atMost(Duration.ofSeconds(10))
.until(() -> fxRateService.getLatestRate() != null);
}
}
mvn test -Dtest=FxRateServiceTest
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
Autowired (Note)
@Autowired is a Spring Framework annotations used for dependency injection. @Autowired tells Spring: "Find a suitable object (bean) and inject it here. Service depending on a repository.
@Repository
public class FxRateRepository {
public double findRate() {
return 4.75;
}
}
@Service
public class FxRateService {
@Autowired
private FxRateRepository repository;
// Equivalent to:
// private FxRateRepository repository = new FxRateRepository();
public double getRate() {
return repository.findRate();
}
}
Constructor injection (BEST PRACTICE).
In modern Spring (Boot 2+), @Autowired is optional if there’s only one constructor.
@Service
public class FxRateService {
private final FxRateRepository repository;
@Autowired
public FxRateService(FxRateRepository repository) {
this.repository = repository;
}
}
Field injection (NOT recommended).
Hard to test, hides dependencies, cannot create object without Spring.
@Autowired
private FxRateRepository repository;