Недавно я помог клиенту начать работу с реализацией Spring Batch. Команда решила перейти к конфигурации на основе JavaConfig для своих пакетных заданий вместо традиционной конфигурации на основе XML. Поскольку это становится все более распространенным подходом к настройке приложений Java, я почувствовал, что пришло время обновить серию Keyhole Spring Batch, чтобы показать вам, как преобразовать существующую конфигурацию Spring Batch на основе XML в новую конфигурацию на основе аннотаций JavaConfig.
 
Уборка дома
Прежде чем мы сможем начать процесс преобразования, мы должны сделать небольшую уборку дома для проекта.
- Обновите свою сборку Java и среду Spring до Java 7, если вы этого еще не сделали.
 - Добавьте зависимость Spring Boot в Maven pom.xml:
 -   Измените версию Spring Batch на 3.0.4.RELEASE и версию Spring Framework на 4.1.6.RELEASE
123
<properties> <spring.framework.version>4.1.6.RELEASE</spring.framework.version><spring.batch.version>3.0.4.RELEASE</spring.batch.version></properties> - Закомментируйте определения заданий в исходном файле конфигурации пакета с именем module-context.xml.
 - Закомментируйте элементы конфигурации контекста приложения Spring в файле конфигурации с именем launch-context.xml.
 - Закомментируйте аннотацию @Component для элементов Reader, Processor и Writer. Не закомментируйте аннотацию @Service в классе CurrencyConversionServiceImpl.
 
| 
 1 
2 
3 
4 
5 
 | 
<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-batch</artifactId>    <version>1.2.4.RELEASE</version></dependency> | 
Создание конфигурации на основе JavaConfig
Теперь, когда мы удалили или отключили существующую конфигурацию на основе XML, мы можем приступить к созданию конфигурации на основе JavaConfig. Для этого нам нужно создать новый класс с некоторыми аннотациями, которые устанавливают основу для конфигурации.
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
 | 
package com.keyhole.example.config;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.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;@Configuration@EnableBatchProcessingpublic class TickerPriceConversionConfig {    @Autowired    private JobBuilderFactory jobs;    @Autowired    private StepBuilderFactory steps;} | 
Аннотация @Configuration позволяет контейнеру Spring знать, что этот класс будет содержать один или несколько аннотированных методов @Bean, которые будут обрабатываться для генерации определений bean-компонентов и запросов на обслуживание во время выполнения.
Аннотация @EnableBatchProcessing предоставляет базовую конфигурацию для создания конфигураций пакетных заданий. Spring Batch использует эту аннотацию для установки заданного по умолчанию JobRepository, JobLauncher, JobRegistry, PlatformTransactionManager, JobBuilderFactory и StepBuilderFactory.
Теперь пришло время добавить наши аннотированные методы @Bean для наших компонентов, которые составляют пакетное задание. Для справки, я включил соответствующую конфигурацию XML для каждого компонента.
Конфигурация ItemReader
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
 | 
<bean name="tickerReader"    class="org.springframework.batch.item.file.FlatFileItemReader"><property name="resource"    <property name="lineMapper" ref="tickerLineMapper" /></bean>  <bean name="tickerLineMapper"class="org.springframework.batch.item.file.mapping.DefaultLineMapper"><property name="fieldSetMapper" ref="tickerMapper" />    <property name="lineTokenizer" ref="tickerLineTokenizer" /></bean>  <bean name="tickerLineTokenizer"class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer" /> | 
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
 | 
@Bean    public ItemReader<TickerData> reader() throws MalformedURLException {        FlatFileItemReader<TickerData> reader = new FlatFileItemReader<TickerData>();        reader.setResource(new UrlResource("http://finance.yahoo.com/d/quotes.csv?s=XOM+IBM+JNJ+MSFT&f=snd1ol1p2"));        reader.setLineMapper(new DefaultLineMapper<TickerData>() {{            setLineTokenizer(new DelimitedLineTokenizer());            setFieldSetMapper(new TickerFieldSetMapper());        }});        return reader;    } | 
ItemProcessor и ItemWriter ранее использовали аннотацию @Component для контейнера Spring, чтобы взять и загрузить bean-компонент в контекст приложения.
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
 | 
@Bean    public ItemProcessor<TickerData, TickerData> processor() {        return new TickerPriceProcessor();    }    @Bean    public ItemWriter<TickerData> writer() {        return new LogItemWriter();    } | 
Теперь, когда мы определили наши bean-компоненты Spring, мы можем создать аннотированные методы @Bean, которые представляют шаг и задание. Для справки я включил соответствующую конфигурацию XML.
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
 | 
<batch:job id="TickerPriceConversion">        <batch:step id="convertPrice">            <batch:tasklet transaction-manager="transactionManager">                <batch:chunk reader="tickerReader"                            processor="tickerPriceProcessor"                        writer="tickerWriter" commit-interval="10" />                </batch:tasklet>            </batch:step>    </batch:job> | 
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
 | 
@Bean    public Job TickerPriceConversion() throws MalformedURLException {        return jobs.get("TickerPriceConversion").start(convertPrice()).build();    }    @Bean        public Step convertPrice() throws MalformedURLException {        return steps.get("convertPrice")                .<TickerData, TickerData> chunk(5)                .reader(reader())                .processor(processor())                .writer(writer())                .build();        } | 
Я включу полный код для класса TickerPriceConversionConfig в конце статьи для справки, но в основном это все, что нужно сделать!
После того как вы определили свои bean-компоненты Spring и использовали JobBuilderFactory и StepBuilderFactory для создания конфигурации bean-компонентов для пакетного задания и шага, вы готовы запустить задание и протестировать его. Для запуска задания мы будем использовать Spring Boot для проверки выполнения недавно преобразованной конфигурации задания. Для этого мы создадим новый класс в тестовом пакете с именем TickerPriceConversionJobRunner.
Исходный код выглядит так:
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
 | 
package com.keyhole.example;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class TickerPriceConversionJobRunner {    public static void main(String[] args) {        SpringApplication.run(TickerPriceConversionJobRunner.class, args);    }} | 
Аннотация @SpringBootApplication — это, по сути, удобная аннотация, предоставляющая функции, которые вы обычно получаете, используя @Configuration, @EnableAutoConfiguration и @ComponentScan. TickerPriceConversionJobRunner — это простое Java-приложение, которое делегирует обработку основного метода классу SpringAot SpringApplication для запуска приложения.
Теперь вы можете экспортировать этот проект в виде jar и запустить TickerPriceConversionJobRunner из командной строки или, если вы хотите запустить его в Spring STS, вы можете щелкнуть правой кнопкой мыши по классу и выбрать «Запуск от имени» → Spring Boot Application.
Заключительные мысли и списки кодов
Как видите, для создания конфигураций заданий Spring Batch не требуется много работы, но если вы решите преобразовать все свои существующие задания из конфигурации на основе XML в более новую конфигурацию на основе JavaConfig, у вас будет довольно впереди немного работы. Большая часть этой работы будет связана с количеством времени, необходимого для адекватного регрессионного тестирования пакетных заданий, которые вы преобразовали.
Будет ли это того стоить, если у вас есть обширная библиотека рабочих мест Spring Batch? Вероятно, нет, но если вы только начинаете или у вас есть управляемая библиотека пакетных заданий, это, безусловно, тот подход, который я бы выбрал.
Листинг кода для TickerPriceConversionConfig
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 
70 
71 
 | 
package com.keyhole.example.config;import java.net.MalformedURLException;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.ItemProcessor;import org.springframework.batch.item.ItemReader;import org.springframework.batch.item.ItemWriter;import org.springframework.batch.item.file.FlatFileItemReader;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.UrlResource;import com.keyhole.example.LogItemWriter;import com.keyhole.example.TickerData;import com.keyhole.example.TickerFieldSetMapper;import com.keyhole.example.TickerPriceProcessor;@Configuration@EnableBatchProcessingpublic class TickerPriceConversionConfig {    @Autowired    private JobBuilderFactory jobs;    @Autowired    private StepBuilderFactory steps;    @Bean    public ItemReader<TickerData> reader() throws MalformedURLException {        FlatFileItemReader<TickerData> reader = new FlatFileItemReader<TickerData>();        reader.setResource(new UrlResource("http://finance.yahoo.com/d/quotes.csv?s=XOM+IBM+JNJ+MSFT&f=snd1ol1p2"));        reader.setLineMapper(new DefaultLineMapper<TickerData>() {{            setLineTokenizer(new DelimitedLineTokenizer());            setFieldSetMapper(new TickerFieldSetMapper());        }});        return reader;    }    @Bean    public ItemProcessor<TickerData, TickerData> processor() {        return new TickerPriceProcessor();    }    @Bean    public ItemWriter<TickerData> writer() {        return new LogItemWriter();    }    @Bean    public Job TickerPriceConversion() throws MalformedURLException {        return jobs.get("TickerPriceConversion").start(convertPrice()).build();    }    @Bean    public Step convertPrice() throws MalformedURLException {        return steps.get("convertPrice")                .<TickerData, TickerData> chunk(5)                .reader(reader())                .processor(processor())                .writer(writer())                .build();    }} | 
Листинг кода для TickerPriceConversionJobRunner
| 
 01 
02 
03 
04 
05 
06 
07 
08 
09 
10 
11 
12 
13 
 | 
package com.keyhole.example;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class TickerPriceConversionJobRunner {    public static void main(String[] args) {        SpringApplication.run(TickerPriceConversionJobRunner.class, args);    }} | 
| Ссылка: | Spring Batch — замена конфигурации задания XML С помощью JavaConfig от нашего партнера по JCG Джонни Хакетта в блоге |