Недавно я помог клиенту начать работу с реализацией 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 Джонни Хакетта в блоге |