Tip
この記事ではSpring Batchのメタデータを保存しない方法を紹介しますが、メタデータを視覚化するためのSpring Batch Dashboardも作ったので、メタデータを保存して有効活用することも検討してください。
Spring Batchでバッチ処理を作成する際に、DB上にメタデータが自動で作成されます。
メタデータを作成したくないという方向けにかつてはMapベースのインメモリJobRepositoryが提供されていましたが、Spring Batch 5で廃止されました。
代わりにH2やHSQLなどのインメモリDBを使うように案内されました。
Note
どちらの場合でもインメモリなJobRepositoryの場合はWebアプリケーションなど常駐プロセス上でバッチを起動する場合には、メモリが増え続けるという問題がありました。
Spring Batch 5.2ではJobRepositoryの実装として、MongoDBに加え、メタデータを保存しないResourcelessJobRepositoryが追加されました。
本記事ではResourcelessJobRepositoryを使いメタデータを保存しないSpring Batchの使用例を紹介します。
Warning
メタデータはJobのリスタートにも使われるので、ResourcelessJobRepositoryを使用する場合は、Jobのリスタートはできません。また、ResourcelessJobRepositoryはスレッドセーフでもありません。
本記事の内容はSpring Boot 3.4.5、Spring Batch 5.2.2で動作確認しました。このバージョンではSpring BatchのAutoConfigurationがデータベースを使用する前提となっています。
そのため、ResourcelessJobRepositoryを使用する場合はAutoConfigurationを無効にする必要があります。これはSpring Batch 6、Spring Boot 4では改善される予定です(spring-batch#4718)。
Spring Bootプロジェクトの作成
Spring Batchのプロジェクトを次のコマンドで作成します。
curl https://start.spring.io/starter.tgz \
-d type=maven-project \
-d artifactId=hello-spring-batch \
-d baseDir=hello-spring-batch \
-d packageName=com.example.hello \
-d dependencies=batch,native \
-d applicationName=HelloSpringBatchApplication | tar -xzvf -
cd hello-spring-batch
Spring BatchのAutoConfigurationを無効にする
前述の通り、Spring BatchのAutoConfigurationを無効にする必要があります。次のようにapplication.propertiesに設定を追加します。
cat <<EOF >> src/main/resources/application.properties
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration
EOF
代わりに、次のようにマニュアルでSpring Batchのコア機能の定義を行います。
cat <<EOF > src/main/java/com/example/hello/BatchConfig.java
package com.example.hello;
import java.lang.reflect.Proxy;
import org.springframework.batch.core.configuration.support.ScopeConfiguration;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.support.TaskExecutorJobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.support.ResourcelessJobRepository;
import org.springframework.boot.autoconfigure.batch.BatchProperties;
import org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.util.StringUtils;
@Configuration(proxyBeanMethods = false)
@Import(ScopeConfiguration.class)
@EnableConfigurationProperties(BatchProperties.class)
public class BatchConfig {
@Bean
public JobRepository jobRepository() {
return new ResourcelessJobRepository();
}
@Bean
public JobLauncher jobLauncher(JobRepository jobRepository) {
TaskExecutorJobLauncher jobLauncher = new TaskExecutorJobLauncher();
jobLauncher.setJobRepository(jobRepository);
return jobLauncher;
}
@Bean
public JobLauncherApplicationRunner jobLauncherApplicationRunner(JobLauncher jobLauncher,
JobRepository jobRepository, BatchProperties properties) {
JobExplorer jobExplorer = (JobExplorer) Proxy.newProxyInstance(JobExplorer.class.getClassLoader(),
new Class<?>[] { JobExplorer.class }, (proxy, method, args) -> null);
JobLauncherApplicationRunner runner = new JobLauncherApplicationRunner(jobLauncher, jobExplorer, jobRepository);
String jobName = properties.getJob().getName();
if (StringUtils.hasText(jobName)) {
runner.setJobName(jobName);
}
return runner;
}
}
EOF
Jobの定義
次のようにJobを定義します。ここはSpring Batchの基本的な使い方と同じですが、PlatformTransactionManagerの実装としてResourcelessTransactionManagerを指定します。
cat <<EOF > src/main/java/com/example/hello/JobConfig.java
package com.example.hello;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class JobConfig {
private final Logger log = LoggerFactory.getLogger(JobConfig.class);
private final JobRepository jobRepository;
public JobConfig(JobRepository jobRepository) {
this.jobRepository = jobRepository;
}
@Bean
@StepScope
public Tasklet helloTasklet() {
return (contribution, chunkContext) -> {
log.info("Hello World!");
return RepeatStatus.FINISHED;
};
}
@Bean
public Step step1(Tasklet helloTasklet) {
return new StepBuilder("step1", jobRepository).tasklet(helloTasklet, new ResourcelessTransactionManager())
.build();
}
@Bean
public Job job1(Step step1) {
return new JobBuilder("job1", jobRepository).start(step1).build();
}
}
EOF
今回はTasklet内でデータベースは扱わないので、Spring Batchにデフォルトで含まれるspring-boot-starter-jdbcの依存を除外します。pom.xmlを編集し、次のように設定します。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</exclusion>
</exclusions>
</dependency>
以上で設定は完了です。
実行
次のコマンドでビルドと実行を行います。
./mvnw clean package -DskipTests
java -jar target/hello-spring-batch-0.0.1-SNAPSHOT.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.4.5)
2025-05-15T01:56:40.031+09:00 INFO 7358 --- [demo] [ main] c.e.hello.HelloSpringBatchApplication : Starting HelloSpringBatchApplication v0.0.1-SNAPSHOT using Java 21.0.6 with PID 7358 (/private/tmp/hello-spring-batch/target/hello-spring-batch-0.0.1-SNAPSHOT.jar started by toshiaki in /private/tmp/hello-spring-batch)
2025-05-15T01:56:40.032+09:00 INFO 7358 --- [demo] [ main] c.e.hello.HelloSpringBatchApplication : No active profile set, falling back to 1 default profile: "default"
2025-05-15T01:56:40.193+09:00 INFO 7358 --- [demo] [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : No TaskExecutor has been set, defaulting to synchronous executor.
2025-05-15T01:56:40.245+09:00 INFO 7358 --- [demo] [ main] c.e.hello.HelloSpringBatchApplication : Started HelloSpringBatchApplication in 0.353 seconds (process running for 0.524)
2025-05-15T01:56:40.246+09:00 INFO 7358 --- [demo] [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: []
2025-05-15T01:56:40.247+09:00 INFO 7358 --- [demo] [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=job1]] launched with the following parameters: [{}]
2025-05-15T01:56:40.249+09:00 INFO 7358 --- [demo] [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [step1]
2025-05-15T01:56:40.252+09:00 INFO 7358 --- [demo] [ main] com.example.hello.JobConfig : Hello World!
2025-05-15T01:56:40.253+09:00 INFO 7358 --- [demo] [ main] o.s.batch.core.step.AbstractStep : Step: [step1] executed in 3ms
2025-05-15T01:56:40.254+09:00 INFO 7358 --- [demo] [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=job1]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 4ms
無事に起動できました。
GraalVM Native Imageでの実行
GraalVM Native Imageで実行する場合は、次のコマンドでビルドと実行を行います。
./mvnw native:compile -Pnative -DskipTests
./target/hello-spring-batch
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.4.5)
2025-05-15T01:58:15.389+09:00 INFO 7554 --- [demo] [ main] c.e.hello.HelloSpringBatchApplication : Starting AOT-processed HelloSpringBatchApplication using Java 21.0.6 with PID 7554 (/private/tmp/hello-spring-batch/target/hello-spring-batch started by toshiaki in /private/tmp/hello-spring-batch)
2025-05-15T01:58:15.389+09:00 INFO 7554 --- [demo] [ main] c.e.hello.HelloSpringBatchApplication : No active profile set, falling back to 1 default profile: "default"
2025-05-15T01:58:15.391+09:00 INFO 7554 --- [demo] [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : No TaskExecutor has been set, defaulting to synchronous executor.
2025-05-15T01:58:15.393+09:00 INFO 7554 --- [demo] [ main] c.e.hello.HelloSpringBatchApplication : Started HelloSpringBatchApplication in 0.01 seconds (process running for 0.019)
2025-05-15T01:58:15.393+09:00 INFO 7554 --- [demo] [ main] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: []
2025-05-15T01:58:15.393+09:00 INFO 7554 --- [demo] [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=job1]] launched with the following parameters: [{}]
2025-05-15T01:58:15.393+09:00 INFO 7554 --- [demo] [ main] o.s.batch.core.job.SimpleStepHandler : Executing step: [step1]
2025-05-15T01:58:15.393+09:00 INFO 7554 --- [demo] [ main] com.example.hello.JobConfig : Hello World!
2025-05-15T01:58:15.393+09:00 INFO 7554 --- [demo] [ main] o.s.batch.core.step.AbstractStep : Step: [step1] executed in
2025-05-15T01:58:15.393+09:00 INFO 7554 --- [demo] [ main] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=job1]] completed with the following parameters: [{}] and the following status: [COMPLETED] in
こちらも問題なく実行できました。