Spring Integration Java DSL

1. Pengenalan

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

Kami akan menggunakan integrasi pemindahan fail yang kami bina dalam Introduction to Spring Integration dan sebaliknya menggunakan DSL.

2. Kebergantungan

Spring Integration Java DSL adalah sebahagian daripada Spring Integration Core.

Oleh itu, kita boleh menambahkan kebergantungan itu:

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

Dan untuk mengerjakan aplikasi pemindahan fail kami, kami juga memerlukan Fail Integrasi Musim Semi:

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

3. Spring Spring Integrasi DSL

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

DSL memperkenalkan beberapa pembangun yang fasih dari mana kita dapat dengan mudah membuat saluran paip Integrasi Musim Semi yang murni di Jawa.

Oleh itu, katakan kita mahu membuat saluran yang merangkumi semua data yang masuk melalui paip.

Pada masa lalu, kita mungkin telah melakukan:

Dan sekarang kita boleh melakukan:

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

4. Aplikasi Pindah Fail

Untuk memulakan integrasi pemindahan fail kami, kami memerlukan beberapa blok bangunan yang mudah.

4.1. Aliran Integrasi

Blok binaan pertama yang kami perlukan adalah aliran integrasi, yang dapat kami perolehi dari pembangun IntegrationFlows :

IntegrationFlows.from(...)

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

  • Sumber Mesej s
  • MessageChannel s, dan
  • Rentetan s

Kita akan bercakap mengenai ketiga-tiganya sebentar lagi.

Selepas kita telah dipanggil dari , beberapa kaedah penyesuaian kini boleh didapati kepada kami:

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

Pada akhirnya, IntegrationFlows akan selalu menghasilkan contoh IntegrationFlow, yang merupakan produk akhir dari mana-mana aplikasi Spring Integration.

Corak mengambil input, melakukan transformasi yang sesuai, dan mengeluarkan hasilnya adalah asas bagi semua aplikasi Spring Integration .

4.2. Memerihalkan Sumber Input

Pertama, untuk memindahkan fail, kita perlu menunjukkan kepada aliran integrasi kita di mana ia harus mencarinya, dan untuk itu, kita memerlukan Sumber Sumber:

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

Ringkasnya, MessageSource adalah tempat dari mana mesej boleh datang yang berada di luar aplikasi.

Lebih khusus lagi, kita memerlukan sesuatu yang dapat menyesuaikan sumber luaran ke dalam perwakilan pesanan musim bunga. Dan kerana penyesuaian ini tertumpu pada input , ini sering disebut Input Channel Adapters.

The bunga-integrasi-fail pergantungan memberikan kita penyesuai saluran input yang hebat untuk kes penggunaan kami: FileReadingMessageSource:

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

Di sini, kami FileReadingMessageSource akan membaca senarai yang diberikan oleh INPUT_DIR dan akan mewujudkan MessageSource daripadanya.

Mari nyatakan ini sebagai sumber kami dalam pemanggilan IntegrationFlows.fore :

IntegrationFlows.from(sourceDirectory());

4.3. Mengkonfigurasi Sumber Input

Sekarang, jika kita memikirkan ini sebagai aplikasi lama, kita mungkin ingin melihat fail ketika masuk , bukan hanya memindahkan fail yang sudah ada di permulaan.

Untuk memudahkan ini, dari juga dapat mengambil konfigurasi tambahan sebagai penyesuaian sumber input lebih lanjut:

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

Dalam kes ini, kita dapat menjadikan sumber input kita lebih tahan dengan memberitahu Spring Integration untuk membuat tinjauan sumber itu - sistem fail kita dalam kes ini - setiap 10 saat.

Dan, sudah tentu, ini tidak terpakai kepada hanya sumber input fail kami, kami boleh menambah Poller ini kepada mana-mana MessageSource .

4.4. Menyaring Mesej 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 pelbagai cara untuk menggunakan Spring Integration Java DSL untuk membina saluran paip integrasi yang berbeza.

Pada dasarnya, kami dapat membuat semula aplikasi pemindahan fail dari tutorial sebelumnya, kali ini menggunakan java tulen.

Kami juga melihat beberapa komponen lain seperti saluran dan jambatan.

Kod sumber lengkap yang digunakan dalam tutorial ini terdapat di Github.