스프링 통합 자바 DSL

1. 소개

이 튜토리얼에서는 애플리케이션 통합을 만들기위한 Spring Integration Java DSL에 대해 배웁니다.

Introduction to Spring Integration에서 빌드 한 파일 이동 통합을 대신 DSL을 사용합니다.

2. 종속성

Spring Integration Java DSL은 Spring Integration Core의 일부입니다.

따라서 해당 종속성을 추가 할 수 있습니다.

 org.springframework.integration spring-integration-core 5.0.6.RELEASE 

그리고 파일 이동 애플리케이션에서 작업하려면 Spring Integration File도 필요합니다.

 org.springframework.integration spring-integration-file 5.0.6.RELEASE 

3. 스프링 통합 자바 DSL

Java DSL 이전에 사용자는 XML로 Spring Integration 구성 요소를 구성했습니다.

DSL은 순수하게 Java로 완전한 Spring Integration 파이프 라인을 쉽게 만들 수있는 유창한 빌더를 소개합니다.

따라서 파이프를 통해 들어오는 모든 데이터를 대문자로 바꾸는 채널을 만들고 싶다고 가정 해 보겠습니다.

과거에는 다음을 수행했을 수 있습니다.

이제 대신 다음을 수행 할 수 있습니다.

@Bean public IntegrationFlow upcaseFlow() { return IntegrationFlows.from("input") .transform(String::toUpperCase) .get(); }

4. 파일 이동 앱

파일 이동 통합을 시작하려면 몇 가지 간단한 구성 요소가 필요합니다.

4.1. 통합 흐름

우리가 필요로하는 첫 번째 빌딩 블록은 IntegrationFlows 빌더 에서 얻을 수있는 통합 흐름입니다 .

IntegrationFlows.from(...)

from 은 여러 유형을 취할 수 있지만이 튜토리얼에서는 세 가지만 살펴볼 것입니다.

  • MessageSource s
  • MessageChannel
  • 문자열 s

세 가지 모두에 대해 곧 이야기하겠습니다.

에서 호출 한 후 이제 몇 가지 사용자 정의 메서드를 사용할 수 있습니다.

IntegrationFlow flow = IntegrationFlows.from(sourceDirectory()) .filter(onlyJpgs()) .handle(targetDirectory()) // add more components .get();

궁극적으로 IntegrationFlows모든 Spring Integration 앱의 최종 제품인 IntegrationFlow 의 인스턴스를 항상 생성합니다 .

입력을 받고, 적절한 변환을 수행하고, 결과를 내보내는이 패턴은 모든 Spring Integration 앱의 기본 입니다.

4.2. 입력 소스 설명

먼저 파일을 이동하려면 통합 흐름에 파일을 찾아야하는 위치를 지정해야하며이를 위해서는 MessageSource 가 필요합니다 .

@Bean public MessageSource sourceDirectory() { // .. create a message source }

간단히 말해 MessageSource 는 애플리케이션 외부에있는 메시지가 올 수있는 장소입니다.

보다 구체적으로, 외부 소스를 Spring 메시징 표현에 적용 할 수 있는 무언가가 필요합니다 . 이 적응입력에 초점을 맞추기 때문에 종종 입력 채널 어댑터 라고 합니다.

스프링 통합 파일의 의존성은 우리에게 우리의 사용 사례에 대한 중대 입력 채널 어댑터를 제공합니다 FileReadingMessageSource를 :

@Bean public MessageSource sourceDirectory() { FileReadingMessageSource messageSource = new FileReadingMessageSource(); messageSource.setDirectory(new File(INPUT_DIR)); return messageSource; }

여기서 FileReadingMessageSourceINPUT_DIR에 의해 제공된 디렉토리를 읽고 여기에서 MessageSource 를 만듭니다 .

IntegrationFlows.from 호출 에서이를 소스로 지정해 보겠습니다 .

IntegrationFlows.from(sourceDirectory());

4.3. 입력 소스 구성

이제 우리가 이것을 수명이 긴 응용 프로그램으로 생각하고 있다면 , 시작할 때 이미있는 파일을 이동하는 것이 아니라 파일이 들어올 때 인식 할 수 있기를 원할 것입니다 .

이를 용이하게하기 위해 from 은 입력 소스의 추가 사용자 정의로 추가 구성 자를 사용할 수도 있습니다 .

IntegrationFlows.from(sourceDirectory(), configurer -> configurer.poller(Pollers.fixedDelay(10000)));

이 경우 Spring Integration에 해당 소스 (이 경우에는 파일 시스템)를 10 초마다 폴링하도록 지시하여 입력 소스를보다 탄력적으로 만들 수 있습니다.

물론 이것은 파일 입력 소스에만 적용되는 것이 아니라이 폴러를 MessageSource에 추가 할 수 있습니다 .

4.4. 입력 소스에서 메시지 필터링

Next, let's suppose we want our file-moving application to move specific files only, say image files having jpg extension.

For this, we can use GenericSelector:

@Bean public GenericSelector onlyJpgs() { return new GenericSelector() { @Override public boolean accept(File source) { return source.getName().endsWith(".jpg"); } }; }

So, let's update our integration flow again:

IntegrationFlows.from(sourceDirectory()) .filter(onlyJpgs());

Or, because this filter is so simple, we could have instead defined it using a lambda:

IntegrationFlows.from(sourceDirectory()) .filter(source -> ((File) source).getName().endsWith(".jpg"));

4.5. Handling Messages With Service Activators

Now that we have a filtered list of files, we need to write them to a new location.

Service Activators are what we turn to when we're thinking about outputs in Spring Integration.

Let's use the FileWritingMessageHandler service activator from spring-integration-file:

@Bean public MessageHandler targetDirectory() { FileWritingMessageHandler handler = new FileWritingMessageHandler(new File(OUTPUT_DIR)); handler.setFileExistsMode(FileExistsMode.REPLACE); handler.setExpectReply(false); return handler; }

Here, our FileWritingMessageHandler will write each Message payload it receives to OUTPUT_DIR.

Again, let's update:

IntegrationFlows.from(sourceDirectory()) .filter(onlyJpgs()) .handle(targetDirectory());

And notice, by the way, the usage of setExpectReply. Because integration flows can bebidirectional, this invocation indicates that this particular pipe is one way.

4.6. Activating Our Integration Flow

When we have added all our components we need to register our IntegrationFlow as a bean to activate it:

@Bean public IntegrationFlow fileMover() { return IntegrationFlows.from(sourceDirectory(), c -> c.poller(Pollers.fixedDelay(10000))) .filter(onlyJpgs()) .handle(targetDirectory()) .get(); }

The get method extracts an IntegrationFlow instance that we need to register as a Spring Bean.

As soon as our application context loads, all our components contained in our IntegrationFlow gets activated.

And now, our application will start moving files from the source directory to target directory.

5. Additional Components

In our DSL-based file-moving application, we created an Inbound Channel Adapter, a Message Filter, and a Service Activator.

Let's look at a few other common Spring Integration components and see how we might use them.

5.1. Message Channels

As mentioned earlier, a Message Channel is another way to initialize a flow:

IntegrationFlows.from("anyChannel")

We can read this as “please find or create a channel bean called anyChannel. Then, read any data that is fed into anyChannel from other flows.”

But, really it is more general-purpose than that.

Simply put, a channel abstracts away producers from consumers, and we can think of it as a Java Queue. A channel can be inserted at any point in the flow.

Let's say, for example, that we want to prioritize the files as they get moved from one directory to the next:

@Bean public PriorityChannel alphabetically() { return new PriorityChannel(1000, (left, right) -> ((File)left.getPayload()).getName().compareTo( ((File)right.getPayload()).getName())); }

Then, we can insert an invocation to channel in between our flow:

@Bean public IntegrationFlow fileMover() { return IntegrationFlows.from(sourceDirectory()) .filter(onlyJpgs()) .channel("alphabetically") .handle(targetDirectory()) .get(); }

There are dozens of channels to pick from, some of the more handy ones being for concurrency, auditing, or intermediate persistence (think Kafka or JMS buffers).

Also, channels can be powerful when combined with Bridges.

5.2. Bridge

When we want to combine two channels, we use a Bridge.

Let's imagine that instead of writing directly to an output directory, we instead had our file-moving app write to another channel:

@Bean public IntegrationFlow fileReader() { return IntegrationFlows.from(sourceDirectory()) .filter(onlyJpgs()) .channel("holdingTank") .get(); }

Now, because we've simply written it to a channel, we can bridge from there to other flows.

Let's create a bridge that polls our holding tank for messages and writes them to a destination:

@Bean public IntegrationFlow fileWriter() { return IntegrationFlows.from("holdingTank") .bridge(e -> e.poller(Pollers.fixedRate(1, TimeUnit.SECONDS, 20))) .handle(targetDirectory()) .get(); }

Again, because we wrote to an intermediate channel, now we can add another flow that takes these same files and writes them at a different rate:

@Bean public IntegrationFlow anotherFileWriter() { return IntegrationFlows.from("holdingTank") .bridge(e -> e.poller(Pollers.fixedRate(2, TimeUnit.SECONDS, 10))) .handle(anotherTargetDirectory()) .get(); }

As we can see, individual bridges can control the polling configuration for different handlers.

As soon as our application context is loaded, we now have a more complex app in action that will start moving files from the source directory to two target directories.

6. Conclusion

이 기사에서는 Spring Integration Java DSL을 사용하여 다양한 통합 파이프 라인을 구축하는 다양한 방법을 살펴 보았습니다.

본질적으로 우리는 이전 튜토리얼에서 파일 이동 애플리케이션을 다시 만들 수 있었는데 이번에는 순수 자바를 사용했습니다.

또한 채널 및 브리지와 같은 몇 가지 다른 구성 요소를 살펴 보았습니다.

이 자습서에서 사용 된 전체 소스 코드는 Github에서 사용할 수 있습니다.