Error Handling Best Practices in Spring Boot with Kotlin
Master Spring Boot error handling in Kotlin with comprehensive examples: learn to implement global exception handlers, custom error responses, and production-ready error handling strategies
Task scheduling is a crucial feature in modern applications, enabling automated execution of tasks at specified intervals or times. Spring Boot provides robust scheduling capabilities that integrate seamlessly with Kotlin applications. In this guide, you'll learn how to implement various scheduling patterns in a Spring Boot application using Kotlin.
To follow along with this tutorial, you'll need:
First, create a new Spring Boot project with Kotlin. You can do this using Spring Initializer (https://start.spring.io) with the following dependencies:
dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
}
Enable scheduling in your application by adding the @EnableScheduling
annotation to your main application class:
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.scheduling.annotation.EnableScheduling
@SpringBootApplication
@EnableScheduling
class SchedulingDemoApplication
fun main(args: Array<String>) {
runApplication<SchedulingDemoApplication>(*args)
}
Let's start with a simple scheduled task that runs at a fixed rate:
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component
import java.time.LocalDateTime
import org.slf4j.LoggerFactory
@Component
class ScheduledTasks {
private val logger = LoggerFactory.getLogger(ScheduledTasks::class.java)
@Scheduled(fixedRate = 5000) // Runs every 5 seconds
fun reportCurrentTime() {
logger.info("Current time is: ${LocalDateTime.now()}")
}
}
This task will run every 5 seconds, logging the current time.
Spring Boot supports several scheduling patterns. Let's explore each one:
Tasks run at a specific interval, regardless of how long the task takes:
@Scheduled(fixedRate = 5000) // 5 seconds
fun fixedRateTask() {
logger.info("Fixed rate task executed")
}
The next execution starts after a fixed delay from the completion of the previous execution:
@Scheduled(fixedDelay = 5000) // 5 seconds delay after completion
fun fixedDelayTask() {
logger.info("Fixed delay task executed")
}
Add an initial delay before the first execution:
@Scheduled(fixedRate = 5000, initialDelay = 10000) // 10 seconds initial delay
fun delayedTask() {
logger.info("Delayed task executed")
}
Use cron expressions for more complex scheduling patterns:
@Scheduled(cron = "0 0 9 * * MON-FRI") // 9 AM on weekdays
fun workdayMorningTask() {
logger.info("Workday morning task executed")
}
For long-running tasks, it's important to handle them asynchronously to prevent blocking:
import org.springframework.scheduling.annotation.Async
import org.springframework.scheduling.annotation.EnableAsync
@Configuration
@EnableAsync
class AsyncConfig {
@Bean
fun taskExecutor(): TaskExecutor {
val executor = ThreadPoolTaskExecutor()
executor.corePoolSize = 2
executor.maxPoolSize = 4
executor.queueCapacity = 10
executor.setThreadNamePrefix("AsyncTask-")
executor.initialize()
return executor
}
}
@Component
class LongRunningTasks {
private val logger = LoggerFactory.getLogger(LongRunningTasks::class.java)
@Async
@Scheduled(fixedRate = 10000)
fun longRunningTask() {
logger.info("Starting long running task")
Thread.sleep(5000) // Simulate long processing
logger.info("Completed long running task")
}
}
Sometimes you need to change scheduling parameters dynamically. Here's how to implement dynamic scheduling:
@Component
class DynamicScheduler {
private val logger = LoggerFactory.getLogger(DynamicScheduler::class.java)
private var scheduler: ThreadPoolTaskScheduler? = null
private var future: ScheduledFuture<*>? = null
@PostConstruct
fun init() {
scheduler = ThreadPoolTaskScheduler().apply {
poolSize = 2
threadNamePrefix = "DynamicScheduler-"
initialize()
}
}
fun startScheduling(intervalInSeconds: Long) {
future?.cancel(false)
future = scheduler?.scheduleAtFixedRate(
{ performTask() },
Duration.ofSeconds(intervalInSeconds)
)
}
fun stopScheduling() {
future?.cancel(false)
}
private fun performTask() {
logger.info("Dynamic task executed at: ${LocalDateTime.now()}")
}
}
Implement proper error handling in your scheduled tasks:
@Component
class ResilientScheduledTasks {
private val logger = LoggerFactory.getLogger(ResilientScheduledTasks::class.java)
@Scheduled(fixedRate = 5000)
fun taskWithErrorHandling() {
try {
performRiskyOperation()
} catch (e: Exception) {
logger.error("Error in scheduled task: ${e.message}")
// Implement recovery logic here
}
}
private fun performRiskyOperation() {
// Your task logic here
}
}
Here's how to test your scheduled tasks:
@SpringBootTest
class ScheduledTasksTest {
@SpyBean
lateinit var scheduledTasks: ScheduledTasks
@Test
fun `verify scheduled task execution`() {
verify(scheduledTasks, timeout(6000).atLeast(1))
.reportCurrentTime()
}
}
@Scheduled(cron = "0 0 9 * * MON-FRI", zone = "Europe/London")
fun timeZoneAwareTask() {
// Task logic
}
Spring Boot's scheduling capabilities provide a powerful way to automate tasks in your Kotlin applications. Whether you need simple fixed-rate execution or complex cron-based scheduling, Spring Boot has you covered. Remember to consider factors like error handling, testing, and resource management when implementing scheduled tasks in your applications.
If your tasks are taking longer than the scheduled interval, you might want to prevent overlap:
@Scheduled(fixedRate = 5000)
@SchedulerLock(name = "preventOverlap")
fun nonOverlappingTask() {
// Task logic
}
Be careful with capturing variables in scheduled tasks:
@Component
class ScheduledTasksWithState {
private val cache = mutableMapOf<String, Any>()
@Scheduled(fixedRate = 5000)
fun cleanupTask() {
// Cleanup old entries
cache.entries.removeIf { it.value.isExpired() }
}
}
For additional help or specific use cases, consult the Spring Framework documentation.
Master Spring Boot error handling in Kotlin with comprehensive examples: learn to implement global exception handlers, custom error responses, and production-ready error handling strategies
Learn how to secure your Spring Boot REST APIs using Kotlin with industry-standard security practices and implementations.
Learn how to implement robust request validation in Spring Boot with Kotlin, from basic field validation to custom validators, complete with practical examples and best practices.