Integrasi Spring Java DSL

1. Perkenalan

Dalam tutorial ini, kita akan belajar tentang Spring Integration Java DSL untuk membuat integrasi aplikasi.

Kami akan mengambil integrasi pemindahan file yang kami buat di Pengantar Integrasi Musim Semi dan menggunakan DSL sebagai gantinya.

2. Dependensi

Spring Integration Java DSL adalah bagian dari Spring Integration Core.

Jadi, kita bisa menambahkan ketergantungan itu:

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

Dan untuk mengerjakan aplikasi pemindahan file kami, kami juga memerlukan File Integrasi Musim Semi:

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

3. Integrasi Spring Java DSL

Sebelum Java DSL, pengguna akan mengkonfigurasi komponen Spring Integration dalam XML.

DSL memperkenalkan beberapa pembuat fasih yang darinya kita dapat dengan mudah membuat pipeline Integrasi Musim Semi lengkap murni di Java.

Jadi, katakanlah kita ingin membuat saluran yang membesarkan semua data yang masuk melalui pipa.

Di masa lalu, kami mungkin telah melakukan:

Dan sekarang kita bisa melakukan:

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

4. Aplikasi Pemindahan File

Untuk memulai integrasi pemindahan file, kita memerlukan beberapa blok penyusun sederhana.

4.1. Alur Integrasi

Blok penyusun pertama yang kita butuhkan adalah alur integrasi, yang bisa kita peroleh dari pembuat IntegrationFlows :

IntegrationFlows.from(...)

dari dapat mengambil beberapa jenis, tetapi dalam tutorial ini, kita hanya akan melihat tiga:

  • MessageSource s
  • MessageChannel s, dan
  • String s

Kami akan segera membicarakan ketiganya.

Setelah kita menelepon dari , beberapa metode penyesuaian sekarang tersedia untuk kita:

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

Pada akhirnya, IntegrationFlows akan selalu menghasilkan instance IntegrationFlow, yang merupakan produk akhir dari aplikasi Integrasi Spring.

Pola pengambilan masukan, melakukan transformasi yang sesuai, dan memancarkan hasil ini sangat penting untuk semua aplikasi Integrasi Musim Semi .

4.2. Menjelaskan Sumber Input

Pertama, untuk memindahkan file, kita perlu menunjukkan ke alur integrasi kita di mana ia harus mencarinya, dan untuk itu, kita memerlukan MessageSource:

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

Sederhananya, MessageSource adalah tempat asal pesan yang bersifat eksternal ke aplikasi.

Lebih khusus lagi, kita membutuhkan sesuatu yang dapat mengadaptasi sumber eksternal tersebut ke dalam representasi perpesanan Spring. Dan karena adaptasi ini difokuskan pada masukan , ini sering disebut Adaptor Saluran Masukan.

The semi-integrasi-berkas ketergantungan memberi kami adaptor saluran input yang bagus untuk kasus penggunaan kami: FileReadingMessageSource:

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

Di sini, FileReadingMessageSource kami akan membaca direktori yang diberikan oleh INPUT_DIR dan akan membuat MessageSource darinya.

Mari tentukan ini sebagai sumber kita di IntegrationFlows.from pemanggilan:

IntegrationFlows.from(sourceDirectory());

4.3. Mengonfigurasi Sumber Input

Sekarang, jika kita menganggap ini sebagai aplikasi berumur panjang, kita mungkin ingin melihat file saat masuk , tidak hanya memindahkan file yang sudah ada saat startup.

Untuk memfasilitasi hal ini, from juga dapat menggunakan konfigurasi tambahan sebagai penyesuaian lebih lanjut dari sumber input:

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

Dalam kasus ini, kita dapat membuat sumber input kita lebih tangguh dengan memberi tahu Spring Integration untuk mengumpulkan sumber tersebut – sistem file kita dalam hal ini – setiap 10 detik.

Dan, tentu saja, ini tidak berlaku hanya untuk sumber input file kami, kami dapat menambahkan poller ini ke MessageSource apa pun .

4.4. Memfilter Pesan dari Sumber Input

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

Dalam artikel ini, kami melihat berbagai cara menggunakan Spring Integration Java DSL untuk membangun pipeline integrasi yang berbeda.

Pada dasarnya, kami dapat membuat ulang aplikasi pemindahan file dari tutorial sebelumnya, kali ini menggunakan java murni.

Selain itu, kami melihat beberapa komponen lain seperti saluran dan jembatan.

Kode sumber lengkap yang digunakan dalam tutorial ini tersedia di Github.