In this tutorial, we will create a really simple Hello World! program with Spring Batch Version 2.1 and learn some basics for this amazing Java batch processing framework.
You will need at least a basic understanding of the Spring (Core) framework to get along with the tutorial.
If you are in a hurry or just the code-first type, you can jump right to the source here on my github repository, dive into the hello-world-java directory.
From the official Spring Batch website:
Spring Batch is a lightweight, comprehensive batch framework designed to enable the development of robust batch applications vital for the daily operations of enterprise systems.
Doesn't that sound like the usual sales pitch? Ok lets try it with (my) own words:
Spring Batch is a framework, which does the grunt work for your Java batch needs and provides lots of ready-to-use implementations to keep your own code minimal.
The official reference covers the Spring Batch concepts in every detail, but for this tutorial we can start with a more simplified view:
A Spring Batch program usually consists of a Job, which encapsulates a flow of steps, where a step can be seen as abstract isolated unit of work. Here that is visualized with a sequential flow example:
A standard Step reads data, processes it and writes data, until the data from the reader is exhausted.
The data is handled with a so called Chunk Oriented Processing approach, where the data is read individually, processed individually, but written in chunks.
The chunks play a role for the transactional behavior too, Spring Batch commits a transaction after each chunk. Thats enough for the basic concepts, time to get ready for some coding.
The Spring Batch framework is really lightweight, but that comes at the prize of a little more complex XML configuration. It is a Spring framework after all.
The configuration needed to get a Spring Batch program running roughly consists of two parts:
- Spring Batch infrastructure
- Spring Batch Job definition
Under the hood Spring Batch works with some tables to keep the state of the jobs and steps and to provide re-startability. The tables are accessed with a DAO called Job Repository and to start a Job the framework provides a Job Launcher. Because a job works with transactions, a transaction manager is mandatory.
To keep this tutorial simple we use a table-less Job Repository and a dummy transaction manager, translated to the Spring and Spring Batch XML configuration it looks like this:
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher"
p:jobRepository-ref="jobRepository" />
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"
p:transactionManager-ref="transactionManager" />
<bean id="transactionManager"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
This is at the same time the most minimal Spring Batch infrastructure configuration possible and does not need any self-made code, all implementations are provided by the framework.
The Job definition - for this tutorial - is quite short:
<job id="helloWorldJob">
<step id="helloWorldStep" >
<tasklet ref="helloWorldTasklet" />
</step>
</job>
<bean id="helloWorldTasklet"
class="de.langmi.spring.batch.tutorials.helloworld.HelloWorldTasklet" />
In contrast to the introduction of the basic concepts, we use a so called Tasklet Step, without the standard Reader, Processor and Writer. A Tasklet Step bypasses the standard Step concept and makes it possible to implement just one method, like printing out Hello World!.
All XML taken from the configurations above and joined in one file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/batch
http://www.springframework.org/schema/batch/spring-batch-2.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<description>
Simple Spring Batch Configuration
- one tasklet step
- prints out "Hello World!"
- setup without database, uses in-memory JobRepository
- not restartable
</description>
<!--
inline xmlns, otherwise it would look like
'batch:job, batch:step, etc.'
-->
<job id="helloWorldJob" xmlns="http://www.springframework.org/schema/batch">
<step id="helloWorldStep" >
<tasklet ref="helloWorldTasklet" />
</step>
</job>
<bean id="helloWorldTasklet"
class="de.langmi.spring.batch.tutorials.helloworld.HelloWorldTasklet" />
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher"
p:jobRepository-ref="jobRepository" />
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"
p:transactionManager-ref="transactionManager" />
<bean id="transactionManager"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
</beans>
In the Job configuration we configured a Tasklet Step, here is the missing source-code:
package de.langmi.spring.batch.tutorials.helloworld;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
public class HelloWorldTasklet implements Tasklet {
/** {@inheritDoc} */
@Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext chunkContext) throws Exception {
// why not using println? because it makes testing harder, *nix and
// windows think different about new line as in \n vs \r\n
System.out.print("Hello World!");
return RepeatStatus.FINISHED;
}
}
And that's it, yes really that and the configuration is a complete Spring Batch.
Before the batch escapes will be released it needs some tests.
The Tasklet implementation is pure Java, so we can test it as that with JUnit:
public class HelloWorldTaskletTest {
private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
private final Tasklet tasklet = new HelloWorldTasklet();
@Test
public void testExecute() throws Exception {
tasklet.execute(null, null);
assertEquals("Hello World!", outContent.toString());
}
@Before
public void setUpStreams() {
// catch system out
System.setOut(new PrintStream(outContent));
}
@After
public void cleanUpStreams() {
// reset JVM standard
System.setOut(null);
}
}
To test the complete Job we can use a lot of the Spring JUnit test utilities. The only difference to a standard Spring test is the use of the Job Launcher, which is needed to launch a job.
@ContextConfiguration(locations = {"classpath*:spring/batch/job/hello-world-job.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class HelloWorldJobConfigurationTest {
@Autowired
private Job job;
@Autowired
private JobLauncher jobLauncher;
private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
/** Simple Launch Test. */
@Test
public void launchJob() throws Exception {
// launch the job
JobExecution jobExecution = jobLauncher.run(job, new JobParameters());
// assert job run status
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
// assert sysoutput
assertEquals("Hello World!", outContent.toString());
}
@Before
public void setUpStreams() {
// catch system out
System.setOut(new PrintStream(outContent));
}
@After
public void cleanUpStreams() {
// reset JVM standard
System.setOut(null);
}
}
This test is essentially an integration test.
To run a Spring Batch Job one can use Spring Batch Admin, which is really out of scope for this tutorial, so we will use the standard Java Main-Class concept with an executable JAR.
The nice thing about Spring frameworks is, right when you need a ready-to-use implementation, the framework has it. We need a Main Class, which can be configured easily and starts the job, here it is: CommandLineJobRunner.
The only thing left is an executable JAR, to create one the pom.xml from the tutorial sources has a configuration for the Maven-shade Plugin. To use it just run the usual: mvn clean install and there will be an hello-world-java-1.0-executable.jar ready for use.
It's about time to end this tutorial, so here is the code to run the job:
java -jar hello-world-java-1.0-executable.jar spring/batch/job/hello-world-job.xml helloWorldJob
If all worked well, you see now a friendly Hello World!.
By following this tutorial you learned some Spring Batch basic concepts and built a simple Hello World! program. The next tutorials will cover more advanced concepts like Read, Process, Write or Logging.
Spring Batch was first introduced in 2007. Back then the framework was created by Interface21 - now known as Springsource - and Accenture. It has seen only 2 major versions so far, but the transition from version 1 to 2 included a massive refactoring of the core concepts. In fact the chunk oriented processing concept was first introduced with Spring Batch version 2.
- tested with:
- Java 1.6
- Maven 3
- Spring Batch 2.1.8.RELEASE
- Spring Framework(core) 3.1.0.RELEASE
- used IDE: primarily programmed with [Netbeans][netbeans] 7.0
- license: Apache 2.0 License
I provided some build manager configurations for:
- Buildr - see buildfile, tested with Buildr 1.4.6
- Gradle - see build.gradle, tested with Gradle 1.0-milestone-5
- Maven - see pom.xml, needs Maven 3+
Each configuration file contains informations on used versions and general setup.
If you work without a build manager you can download all needed libraries from Spring Batch downloads, but remember this tutorial is tested with Spring Batch Version 2.1 only.




