1. Введение
Spring Integration 4.0 наконец-то здесь , и этот выпуск поставляется с очень приятными функциями. В этой статье рассматривается возможность настройки потока интеграции без использования XML вообще. Те, кто не любит XML, смогут разработать интеграционное приложение, используя только JavaConfig.
Эта статья состоит из следующих разделов:
- Введение.
- Обзор потока.
- Весенняя комплектация.
- Деталь конечных точек.
- Тестирование всего потока.
- Вывод.
- Исходный код можно найти на github .
- Исходный код веб-службы, вызванной в этом примере, можно найти в репозитории spring-samples на github .
2. Обзор потока
В примере приложения показано, как настроить несколько конечных точек обмена сообщениями и интеграции. Пользователь запрашивает курс, указав идентификатор курса. Поток вызовет веб-сервис и вернет ответ пользователю. Кроме того, некоторые типы курсов будут сохранены в базе данных.
Поток выглядит следующим образом:
- Шлюз интеграции (служба курса) служит входом в систему обмена сообщениями.
- Преобразователь создает сообщение запроса от указанного пользователем идентификатора курса.
- Исходящий шлюз веб-службы отправляет запрос веб-службе и ожидает ответа.
- Активатор службы подписан на канал ответа, чтобы вернуть имя курса пользователю.
- Фильтр также подписан на канал ответа. Этот фильтр отправляет некоторые типы курсов на адаптер канала mongodb для сохранения ответа в базе данных.
Следующая диаграмма лучше показывает, как структурирован поток:
3. Пружинная конфигурация
Как обсуждалось в разделе введения, вся конфигурация определяется с помощью JavaConfig. Эта конфигурация разделена на три файла: инфраструктура, веб-сервис и конфигурация базы данных. Давайте проверим это:
3.1 Конфигурация инфраструктуры
Этот файл конфигурации содержит только определение каналов сообщений. Конечные точки обмена сообщениями (преобразователь, фильтр и т. Д.) Настроены с аннотациями.
InfrastructureConfiguration.java
|
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
|
@Configuration@ComponentScan("xpadro.spring.integration.endpoint") //@Component@IntegrationComponentScan("xpadro.spring.integration.gateway") //@MessagingGateway@EnableIntegration@Import({MongoDBConfiguration.class, WebServiceConfiguration.class})public class InfrastructureConfiguration { @Bean @Description("Entry to the messaging system through the gateway.") public MessageChannel requestChannel() { return new DirectChannel(); } @Bean @Description("Sends request messages to the web service outbound gateway") public MessageChannel invocationChannel() { return new DirectChannel(); } @Bean @Description("Sends web service responses to both the client and a database") public MessageChannel responseChannel() { return new PublishSubscribeChannel(); } @Bean @Description("Stores non filtered messages to the database") public MessageChannel storeChannel() { return new DirectChannel(); }} |
Аннотация @ComponentScan ищет аннотированные классы @Component, которые являются нашими определенными конечными точками обмена сообщениями; фильтр, трансформатор и сервисный активатор.
Аннотация @IntegrationComponentScan выполняет поиск конкретных примечаний по интеграции. В нашем примере он будет сканировать шлюз входа, аннотированный @MessagingGateway.
Аннотация @EnableIntegration позволяет настраивать интеграцию. Например, аннотации уровня метода, такие как @Transformer или @Filter.
3.2 Конфигурация веб-сервиса
Этот файл конфигурации настраивает исходящий шлюз веб-службы и его требуемый маршаллер.
WebServiceConfiguration.java
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
@Configurationpublic class WebServiceConfiguration { @Bean @ServiceActivator(inputChannel = "invocationChannel") public MessageHandler wsOutboundGateway() { MarshallingWebServiceOutboundGateway gw = new MarshallingWebServiceOutboundGateway("http://localhost:8080/spring-ws-courses/courses", jaxb2Marshaller()); gw.setOutputChannelName("responseChannel"); return gw; } @Bean public Jaxb2Marshaller jaxb2Marshaller() { Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); marshaller.setContextPath("xpadro.spring.integration.ws.types"); return marshaller; }} |
Шлюз позволяет нам определять его выходной канал, но не входной канал. Нам нужно аннотировать адаптер с помощью @ServiceActivator, чтобы подписать его на канал вызова и избежать необходимости автоматического подключения его в определении компонента канала сообщений.
3.3 Конфигурация базы данных
Этот файл конфигурации определяет все необходимые bean-компоненты для настройки mongoDB . Он также определяет адаптер исходящего канала mongoDB.
MongoDBConfiguration.java
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
@Configurationpublic class MongoDBConfiguration { @Bean public MongoDbFactory mongoDbFactory() throws Exception { return new SimpleMongoDbFactory(new MongoClient(), "si4Db"); } @Bean @ServiceActivator(inputChannel = "storeChannel") public MessageHandler mongodbAdapter() throws Exception { MongoDbStoringMessageHandler adapter = new MongoDbStoringMessageHandler(mongoDbFactory()); adapter.setCollectionNameExpression(new LiteralExpression("courses")); return adapter; }} |
Как и шлюз веб-службы, мы не можем установить входной канал для адаптера. Я также сделал это, указав входной канал в аннотации @ServiceActivator.
4. Детализация конечных точек
Первой конечной точкой потока является шлюз интеграции, который поместит аргумент (courseId) в полезную нагрузку сообщения и отправит его в канал запроса.
|
1
2
3
4
5
|
@MessagingGateway(name = "entryGateway", defaultRequestChannel = "requestChannel")public interface CourseService { public String findCourse(String courseId);} |
Сообщение, содержащее идентификатор курса, достигнет преобразователя. Эта конечная точка создаст объект запроса, который ожидает веб-служба:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
@Componentpublic class CourseRequestBuilder { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Transformer(inputChannel="requestChannel", outputChannel="invocationChannel") public GetCourseRequest buildRequest(Message<String> msg) { logger.info("Building request for course [{}]", msg.getPayload()); GetCourseRequest request = new GetCourseRequest(); request.setCourseId(msg.getPayload()); return request; }} |
Подписанный на канал ответа, который является каналом, на который будет отправляться ответ веб-службы, существует активатор службы, который получит ответное сообщение и доставит имя курса клиенту:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
@Componentpublic class CourseResponseHandler { private Logger logger = LoggerFactory.getLogger(this.getClass()); @ServiceActivator(inputChannel="responseChannel") public String getResponse(Message<GetCourseResponse> msg) { GetCourseResponse course = msg.getPayload(); logger.info("Course with ID [{}] received: {}", course.getCourseId(), course.getName()); return course.getName(); }} |
Также подписанный на канал ответа, фильтр будет определять на основе его типа, если курс должен быть сохранен в базе данных:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
@Componentpublic class StoredCoursesFilter { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Filter(inputChannel="responseChannel", outputChannel="storeChannel") public boolean filterCourse(Message<GetCourseResponse> msg) { if (!msg.getPayload().getCourseId().startsWith("BC-")) { logger.info("Course [{}] filtered. Not a BF course", msg.getPayload().getCourseId()); return false; } logger.info("Course [{}] validated. Storing to database", msg.getPayload().getCourseId()); return true; }} |
5. Тестирование всего потока
Следующий клиент отправит два запроса; запрос курса типа BC, который будет сохранен в базе данных, и курс типа DF, который будет окончательно отфильтрован:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes={InfrastructureConfiguration.class})public class TestApp { @Autowired CourseService service; @Test public void testFlow() { String courseName = service.findCourse("BC-45"); assertNotNull(courseName); assertEquals("Introduction to Java", courseName); courseName = service.findCourse("DF-21"); assertNotNull(courseName); assertEquals("Functional Programming Principles in Scala", courseName); }} |
Это приведет к следующему выводу консоли:
|
01
02
03
04
05
06
07
08
09
10
11
|
CourseRequestBuilder|Building request for course [BC-45]CourseResponseHandler|Course with ID [BC-45] received: Introduction to JavaStoredCoursesFilter|Course [BC-45] validated. Storing to databaseCourseRequestBuilder|Building request for course [DF-21]CourseResponseHandler|Course with ID [DF-21] received: Functional Programming Principles in ScalaStoredCoursesFilter|Course [DF-21] filtered. Not a BF course |
6. Заключение
Мы узнали, как настроить и протестировать приложение на основе Spring Integration без использования XML-конфигурации. Оставайтесь с нами, потому что Spring Integration Java DSL с расширениями Spring Integration уже в пути!
| Ссылка: | Spring Integration 4.0: полный пример без XML от нашего партнера по |
