Source Code for this blog can be found here
Refer this before you get started with Spring Cloud Task
Spring Cloud Task helps create short lived microservices for spring boot application, which can be preferably used for Kubernetes Job/Cronjob
There are two steps :
Enabling Task
Add @EnableTask Annotation to your Application class along with @SpringBootApplication

Implement Task
In spring boot we can execute any task just before before application finishes its startup, by providing an implementation of org.springframework.boot.CommandLineRunner or org.springframework.boot.ApplicationRunner, these task either should be a @Component or returned from @Bean method.
Spring Cloud Task Internal Implementation Detail

@EnableTask import TaskLifecycleConfiguration, which is a Configuration class for TaskRepository, TaskExplorer, TaskLifecycleListener and so on..


SimpleTaskAutoConfiguration is responsible for configuring TaskConfigurer (org.springframework.cloud.task.configuration.DefaultTaskConfigurer)

Hello World
https://start.spring.io/


Note : Here we are using H2 In memory database, which is fine for development and not for production, for production use a persistence database instance which stores all records or your task executions, DDLs can be found here

application.properties
spring.application.name=Trigger Batch process Job
spring.cloud.task.closecontextEnabled=true
spring.cloud.task.initialize-enabled=true
spring.cloud.task.single-instance-enabled=false
#spring.cloud.task.name=
logging.level.org.springframework.cloud.task=DEBUG
logging.level.com.org.lob=DEBUG
All the exposed properties are defined in class org.springframework.cloud.task.configuration.TaskProperties
TaskConfig.java
package com.org.lob.config;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TaskConfig {
}
TriggerBatchProcessJob.java
package com.org.lob.job;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class TriggerBatchProcessJob implements CommandLineRunner {
private static final Logger LOGGER = LoggerFactory.getLogger(TriggerBatchProcessJob.class);
@Override
public void run(String... args) throws Exception {
LOGGER.info("Hello, World!");
}
}
Run the application
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.0)
2021-05-29 16:49:25.670 INFO 12780 --- [ main] c.o.l.TriggerBatchProcessJobApplication : Starting TriggerBatchProcessJobApplication using Java 15.0.1 on DESKTOP-9AES3TT with PID 12780 (E:\eWorkspaces\job\lob-proj-job-trigger-batch-process\target\classes started by reach in E:\eWorkspaces\job\lob-proj-job-trigger-batch-process)
2021-05-29 16:49:25.672 INFO 12780 --- [ main] c.o.l.TriggerBatchProcessJobApplication : No active profile set, falling back to default profiles: default
2021-05-29 16:49:26.299 INFO 12780 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2021-05-29 16:49:26.427 INFO 12780 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2021-05-29 16:49:26.440 DEBUG 12780 --- [ main] o.s.c.t.c.SimpleTaskAutoConfiguration : Using org.springframework.cloud.task.configuration.DefaultTaskConfigurer TaskConfigurer
2021-05-29 16:49:26.441 DEBUG 12780 --- [ main] o.s.c.t.c.DefaultTaskConfigurer : No EntityManager was found, using DataSourceTransactionManager
2021-05-29 16:49:26.557 DEBUG 12780 --- [ main] o.s.c.t.r.s.TaskRepositoryInitializer : Initializing task schema for h2 database
2021-05-29 16:49:26.638 DEBUG 12780 --- [ main] o.s.c.t.r.support.SimpleTaskRepository : Creating: TaskExecution{executionId=0, parentExecutionId=null, exitCode=null, taskName='Trigger Batch process Job', startTime=Sat May 29 16:49:26 IST 2021, endTime=null, exitMessage='null', externalExecutionId='null', errorMessage='null', arguments=[]}
2021-05-29 16:49:26.657 INFO 12780 --- [ main] c.o.l.TriggerBatchProcessJobApplication : Started TriggerBatchProcessJobApplication in 1.283 seconds (JVM running for 1.598)
2021-05-29 16:49:26.658 INFO 12780 --- [ main] o.s.b.a.ApplicationAvailabilityBean : Application availability state LivenessState changed to CORRECT
Hello, World!
2021-05-29 16:49:26.672 DEBUG 12780 --- [ main] o.s.c.t.r.support.SimpleTaskRepository : Updating: TaskExecution with executionId=1 with the following {exitCode=0, endTime=Sat May 29 16:49:26 IST 2021, exitMessage='null', errorMessage='null'}
2021-05-29 16:49:26.674 INFO 12780 --- [ main] o.s.b.a.ApplicationAvailabilityBean : Application availability state ReadinessState changed to ACCEPTING_TRAFFIC
Task Execution Listener
Refer this for more details
BatchProcessTaskListener.java
package com.org.lob.job;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.task.listener.annotation.AfterTask;
import org.springframework.cloud.task.listener.annotation.BeforeTask;
import org.springframework.cloud.task.listener.annotation.FailedTask;
import org.springframework.cloud.task.repository.TaskExecution;
public class BatchProcessTaskListener {
private static final Logger LOGGER = LoggerFactory.getLogger(BatchProcessTaskListener.class);
@BeforeTask
public void beforeTask(TaskExecution taskExecution) {
LOGGER.debug("Starting Job `{}`", taskExecution.getTaskName());
}
@AfterTask
public void afterTask(TaskExecution taskExecution) {
LOGGER.debug("Finished Job `{}`", taskExecution.getTaskName());
}
@FailedTask
public void failedTask(TaskExecution taskExecution, Throwable throwable) {
LOGGER.error("Error for job `{}`", taskExecution.getTaskName(), throwable);
}
}
TaskConfig.java
package com.org.lob.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.org.lob.job.BatchProcessTaskListener;
@Configuration
public class TaskConfig {
@Bean
public BatchProcessTaskListener taskListener() {
return new BatchProcessTaskListener();
}
}

Run the Application
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.0)
2021-05-29 17:15:23.868 INFO 13004 --- [ main] c.o.l.TriggerBatchProcessJobApplication : Starting TriggerBatchProcessJobApplication using Java 15.0.1 on DESKTOP-9AES3TT with PID 13004 (E:\eWorkspaces\job\lob-proj-job-trigger-batch-process\target\classes started by reach in E:\eWorkspaces\job\lob-proj-job-trigger-batch-process)
2021-05-29 17:15:23.870 DEBUG 13004 --- [ main] c.o.l.TriggerBatchProcessJobApplication : Running with Spring Boot v2.5.0, Spring v5.3.7
2021-05-29 17:15:23.870 INFO 13004 --- [ main] c.o.l.TriggerBatchProcessJobApplication : No active profile set, falling back to default profiles: default
2021-05-29 17:15:24.548 INFO 13004 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2021-05-29 17:15:24.670 INFO 13004 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2021-05-29 17:15:24.681 DEBUG 13004 --- [ main] o.s.c.t.c.SimpleTaskAutoConfiguration : Using org.springframework.cloud.task.configuration.DefaultTaskConfigurer TaskConfigurer
2021-05-29 17:15:24.682 DEBUG 13004 --- [ main] o.s.c.t.c.DefaultTaskConfigurer : No EntityManager was found, using DataSourceTransactionManager
2021-05-29 17:15:24.805 DEBUG 13004 --- [ main] o.s.c.t.r.s.TaskRepositoryInitializer : Initializing task schema for h2 database
2021-05-29 17:15:24.900 DEBUG 13004 --- [ main] o.s.c.t.r.support.SimpleTaskRepository : Creating: TaskExecution{executionId=0, parentExecutionId=null, exitCode=null, taskName='Trigger Batch process Job', startTime=Sat May 29 17:15:24 IST 2021, endTime=null, exitMessage='null', externalExecutionId='null', errorMessage='null', arguments=[]}
2021-05-29 17:15:24.913 DEBUG 13004 --- [ main] c.org.lob.job.BatchProcessTaskListener : Starting Job `Trigger Batch process Job`
2021-05-29 17:15:24.922 INFO 13004 --- [ main] c.o.l.TriggerBatchProcessJobApplication : Started TriggerBatchProcessJobApplication in 1.394 seconds (JVM running for 1.726)
2021-05-29 17:15:24.923 INFO 13004 --- [ main] o.s.b.a.ApplicationAvailabilityBean : Application availability state LivenessState changed to CORRECT
2021-05-29 17:15:24.924 INFO 13004 --- [ main] com.org.lob.job.TriggerBatchProcessJob : Hello, World!
2021-05-29 17:15:24.928 DEBUG 13004 --- [ main] c.org.lob.job.BatchProcessTaskListener : Finished Job `Trigger Batch process Job`
2021-05-29 17:15:24.939 DEBUG 13004 --- [ main] o.s.c.t.r.support.SimpleTaskRepository : Updating: TaskExecution with executionId=1 with the following {exitCode=0, endTime=Sat May 29 17:15:24 IST 2021, exitMessage='null', errorMessage='null'}
2021-05-29 17:15:24.944 INFO 13004 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2021-05-29 17:15:24.949 INFO 13004 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
Change To MySQL from H2

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.org.lob</groupId>
<artifactId>lob-proj-job-trigger-batch-process</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>lob-proj-job-trigger-batch-process</name>
<description>Spring Cloud Task as Kubernetes CronJob</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2020.0.3-SNAPSHOT</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-task</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
application.properties
spring.application.name=Trigger Batch process Job
spring.cloud.task.closecontextEnabled=true
spring.cloud.task.initialize-enabled=false
spring.cloud.task.single-instance-enabled=false
#spring.cloud.task.name=
# MySQL DB
spring.datasource.url=jdbc:mysql://${DB_SERVER}:${DB_PORT}/${DB_NAME}
spring.datasource.username=${DB_USER}
spring.datasource.password=${DB_PASS}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
# RabbitMQ
spring.rabbitmq.host=${RMQ_HOST}
spring.rabbitmq.port=${RMQ_PORT}
spring.rabbitmq.username=${RMQ_USER}
spring.rabbitmq.password=${RMQ_PASS}
logging.level.org.springframework.cloud.task=DEBUG
logging.level.com.org.lob=DEBUG
Get the project from here, it has docker-compose.xml and Dockerfile, and execute the following commands
mvn clean package
docker compose up --build


Once the db is up you can run the application from eclipse as well.




Kubernetes CronJob
With the help of CI/CD pipeline and image can be easily created, and use in Kubernetes CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: tigger-batch-process
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: tigger-batch-process
image: ${IMAGE_NAME}
imagePullPolicy: IfNotPresent
restartPolicy: OnFailure
The code achieves the following





