Spring Batch a Java konfigurace

Doposud jsem v příspěvcích k Spring Batch používal xml konfiguraci. Spring ale umožňuje též konfigurovat pomocí java tříd. V tomto příspěvku ukáži jednoduchý Spring Batch projekt, který bude používat java config. Projekt bude stejný jako v tomto příspěvku, pouze bude místo xml používat java konfiguraci.

Struktura projektu

│   pom.xml
│
└───src
    ├───main
    │   └───java
    │       └───batch
    │               AppConfig.java
    │               Employee.java
    │               ExecuteBatchJob.java
    │               ResultWriter.java
    │
    └───resources
            reportCS.csv

AppConfig.java předstaje java třídu obsahující konfiguraci.

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

@Configuration
@EnableBatchProcessing
public class AppConfig {
	
	@Autowired
	public JobBuilderFactory jobBuilderFactory;
	
	@Autowired
	public StepBuilderFactory stepBuilderFactory;
	
	@Bean
	public Job myJob() {
		return jobBuilderFactory.get("myJob")
				.start(myStep())
				.build();
	}
	
	@Bean
	public Step myStep() {
		return stepBuilderFactory.get("myStep")
				.<Employee, Employee> chunk(2)
				.reader(csvReader())
				.writer(resultWriter())
				.build();
	}
	
	@Bean
	public ItemReader<Employee> csvReader() {
		FlatFileItemReader<Employee> reader = new FlatFileItemReader<Employee>();
		reader.setResource(new ClassPathResource("reportCS.csv"));
		reader.setEncoding("UTF-8");
		reader.setLinesToSkip(1);
		reader.setLineMapper(lineMapper());
		return reader;
	}
	
	@Bean
	public LineMapper<Employee> lineMapper() {
		DefaultLineMapper<Employee> lineMapper = new DefaultLineMapper<Employee>();
		
		// create line tokenizer
		DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
		lineTokenizer.setNames(new String[] {"Name", "Surname", "Location", "Job", "Earning"});
		lineMapper.setLineTokenizer(lineTokenizer);
		
		// create field set mapper
		BeanWrapperFieldSetMapper<Employee> fieldSetMapper = new BeanWrapperFieldSetMapper<Employee>();
		fieldSetMapper.setTargetType(Employee.class);
		lineMapper.setFieldSetMapper(fieldSetMapper);
		
		return lineMapper;
	}
	
	@Bean
	public ResultWriter resultWriter() {
		return new ResultWriter();
	}
}

Spring anotace @Configuration naznačuje, že tato třída obsahuje definici jedné nebo více bean. Anotace @EnableBatchProcessing provádí základní konfiguraci batche. Tato anotace například zajistí autowirování JobBuilderFactory a StepBuilderFactory. Anotace @Bean označuje metodu, která vytváří beanu pro Spring kontejner. Tato anotace odpovídá xml elementu <bean></bean>.

Tato část jobBuilderFactory.get("myJob").start(myStep()).build(); vytváří Job s názvem „myJob“, který obsahuje jeden Step, který se získá voláním metody myStep().

Tato část stepBuilderFactory.get("myStep").<Employee, Employee> chunk(2).reader(csvReader()).writer(resultWriter()).build(); vytváří Step s názvem „myStep“, který obsahuje Chunk s commit intervalem 2. Tento chunk má definovaný ItemReader, který vrací metoda csvReader() a ItemWriter, který vrací metoda resultWriter().

Employee.java

public class Employee {

	private String name;
	private String surname;
	private String location;
	private String job;
	private String earning;

	// setters and getters
}

ResultWriter.java

import java.util.List;

import org.springframework.batch.item.ItemWriter;

public class ResultWriter implements ItemWriter<Employee>{

	public void write(List<? extends Employee> items) throws Exception {
		System.out.println("Writing");
		for (Employee item : items) {
			System.out.println(item.getSurname() + "\t" + item.getEarning());
		}
	}
}

ExecuteBatchJob.java

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class ExecuteBatchJob {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
		
		JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
		Job job = (Job) context.getBean("myJob");
		
		try {
			JobExecution execution = jobLauncher.run(job, new JobParameters());
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			context.close();
		}
	}
}

Narozdíl od xml konfigurace, kde se context vytváří takto ApplicationContext context = new ClassPathXmlApplicationContext("config.xml"); se v případě java konfigurace používá AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);, které se jako parametr předá třída s konfigurací.

Pro úplnost ještě soubor reportCS.csv

Name,Surname,Location,Job,Earning
Václav,Zelený,Čáslav,skladník,22000
Jana,Skočdopolová,Praha,účetní,27900
Jiří,Nohavička,Aš,prodavač,19000
Jan,Starý,Náměšť nad Oslavou,manažer,33000
Tereza,Nová,Benešov,kuchařka,14000

Výsledek spuštění

Writing
Zelený	        22000
Skočdopolová	27900
Writing
Nohavička	19000
Starý	        33000
Writing
Nová	        14000

Napsat komentář