Antaramuka Penyedia Perkhidmatan Java

1. Gambaran keseluruhan

Java 6 telah memperkenalkan fitur untuk mencari dan memuat implementasi yang sesuai dengan antara muka tertentu: Antaramuka Penyedia Perkhidmatan (SPI)

Dalam tutorial ini, kami akan memperkenalkan komponen Java SPI dan menunjukkan bagaimana kami dapat menerapkannya pada sarung penggunaan praktikal.

2. Syarat dan Definisi SPI Java

Java SPI mentakrifkan empat komponen utama

2.1. Perkhidmatan

Satu set antara muka dan kelas pengaturcaraan yang terkenal yang memberikan akses kepada beberapa fungsi atau ciri aplikasi tertentu.

2.2. Antaramuka Pembekal Perkhidmatan

Antaramuka atau kelas abstrak yang bertindak sebagai proksi atau titik akhir perkhidmatan.

Sekiranya perkhidmatan adalah satu antara muka, maka ia sama dengan antara muka penyedia perkhidmatan.

Perkhidmatan dan SPI bersama-sama terkenal di Ekosistem Java sebagai API.

2.3. Pembekal perkhidmatan

Pelaksanaan SPI khusus. Penyedia Perkhidmatan mengandungi satu atau lebih kelas konkrit yang melaksanakan atau memperluas jenis perkhidmatan.

Penyedia Perkhidmatan dikonfigurasi dan dikenal pasti melalui fail konfigurasi penyedia yang kami masukkan ke dalam direktori sumber META-INF / perkhidmatan . Nama fail adalah nama SPI yang memenuhi syarat sepenuhnya dan kandungannya adalah nama pelaksanaan SPI yang berkelayakan sepenuhnya.

Penyedia Perkhidmatan dipasang dalam bentuk sambungan, fail jar yang kami letakkan di classpath aplikasi, classpath sambungan Java atau classpath yang ditentukan pengguna.

2.4. Pemuat Perkhidmatan

Di tengah-tengah SPI adalah kelas ServiceLoader . Ini berperanan untuk mencari dan memuatkan implementasi dengan malas. Ia menggunakan konteks classpath untuk mencari pelaksanaan penyedia dan memasukkannya ke dalam cache dalaman.

3. Sampel SPI di Ekosistem Jawa

Java menyediakan banyak SPI. Berikut adalah beberapa contoh antara muka penyedia perkhidmatan dan perkhidmatan yang disediakannya:

  • CurrencyNameProvider: menyediakan simbol mata wang tempatan untuk kelas Mata Wang .
  • LocaleNameProvider: memberikan nama yang dilokalkan untuk kelas Locale .
  • TimeZoneNameProvider: menyediakan nama zon waktu yang dilokalkan untuk kelas TimeZone .
  • DateFormatProvider: menyediakan format tarikh dan masa untuk tempat yang ditentukan.
  • NumberFormatProvider: memberikan nilai wang, integer dan peratusan untuk kelas NumberFormat .
  • Pemacu: pada versi 4.0, JDBC API menyokong corak SPI. Versi lama menggunakan kaedah Class.forName () untuk memuatkan pemacu.
  • PersistenceProvider: menyediakan pelaksanaan API JPA.
  • JsonProvider: menyediakan objek pemprosesan JSON.
  • JsonbProvider: menyediakan objek pengikat JSON.
  • Sambungan: menyediakan sambungan untuk bekas CDI.
  • ConfigSourceProvider : menyediakan sumber untuk mendapatkan semula sifat konfigurasi.

4. Showcase: Aplikasi Kadar Pertukaran Mata Wang

Sekarang setelah kita memahami asas-asasnya, mari kita jelaskan langkah-langkah yang diperlukan untuk membuat aplikasi kadar pertukaran.

Untuk menyoroti langkah-langkah ini, kita perlu menggunakan sekurang-kurangnya tiga projek: pertukaran-api-api , pertukaran-nilai-impl, dan nilai-aplikasi-aplikasi.

Pada sub-bahagian 4.1., Kita akan membahas Perkhidmatan , SPI dan ServiceLoader melalui api-rate-api modul , kemudian di sub-bahagian 4.2. kami akan melaksanakan penyedia perkhidmatan kami dalam modul kadar pertukaran , dan akhirnya, kami akan mengumpulkan semuanya dalam sub-bahagian 4.3 melalui aplikasi kadar pertukaran modul .

Malah, kami boleh menyediakan seberapa banyak modul seperti yang kita perlu untuk se rvice pembekal dan menjadikan mereka tersedia di classpath modul kadar pertukaran-app.

4.1. Membina API Kami

Kita mulakan dengan membuat projek Maven yang disebut pertukaran-api-api . Amalan baik bahawa namanya diakhiri dengan istilah api , tetapi kita boleh menyebutnya apa sahaja.

Kemudian kami membuat kelas model untuk mewakili mata wang kadar:

package com.baeldung.rate.api; public class Quote { private String currency; private LocalDate date; ... }

Dan kemudian kami menentukan Perkhidmatan kami untuk mengambil sebut harga dengan membuat antara muka QuoteManager:

package com.baeldung.rate.api public interface QuoteManager { List getQuotes(String baseCurrency, LocalDate date); }

Seterusnya, kami membuat SPI untuk perkhidmatan kami:

package com.baeldung.rate.spi; public interface ExchangeRateProvider { QuoteManager create(); }

Dan akhirnya, kita perlu membuat kelas utiliti ExchangeRate.java yang boleh digunakan oleh kod pelanggan. Kelas ini mewakilkan ke ServiceLoader .

Pertama, kami meminta beban kaedah kilang statik () untuk mendapatkan contoh ServiceLoader:

ServiceLoader loader = ServiceLoader .load(ExchangeRateProvider.class); 

Dan kemudian kami menggunakan kaedah iterate () untuk mencari dan mengambil semua pelaksanaan yang ada.

Iterator = loader.iterator(); 

Hasil carian di-cache sehingga kami dapat menggunakan kaedah ServiceLoader.reload () untuk mengetahui implementasi yang baru dipasang:

Iterator = loader.reload(); 

Dan inilah kelas utiliti kami:

public class ExchangeRate { ServiceLoader loader = ServiceLoader .load(ExchangeRateProvider.class); public Iterator providers(boolean refresh) { if (refresh) { loader.reload(); } return loader.iterator(); } }

Sekarang kami mempunyai perkhidmatan untuk mendapatkan semua implementasi yang terpasang, kami dapat menggunakan semuanya dalam kod pelanggan kami untuk memperluas aplikasi kami atau hanya dengan memilih implementasi yang disukai.

Note that this utility class is not required to be part of the api project. Client code can choose to invoke ServiceLoader methods itself.

4.2. Building the Service Provider

Let's now create a Maven project named exchange-rate-impl and we add the API dependency to the pom.xml:

 com.baeldung exchange-rate-api 1.0.0-SNAPSHOT 

Then we create a class that implements our SPI:

public class YahooFinanceExchangeRateProvider implements ExchangeRateProvider { @Override public QuoteManager create() { return new YahooQuoteManagerImpl(); } }

And here the implementation of the QuoteManager interface:

public class YahooQuoteManagerImpl implements QuoteManager { @Override public List getQuotes(String baseCurrency, LocalDate date) { // fetch from Yahoo API } }

In order to be discovered, we create a provider configuration file:

META-INF/services/com.baeldung.rate.spi.ExchangeRateProvider 

The content of the file is the fully qualified class name of the SPI implementation:

com.baeldung.rate.impl.YahooFinanceExchangeRateProvider 

4.3. Putting It Together

Finally, let's create a client project called exchange-rate-app and add the dependency exchange-rate-api to the classpath:

 com.baeldung exchange-rate-api 1.0.0-SNAPSHOT 

At this point, we can call the SPI from our application:

ExchangeRate.providers().forEach(provider -> ... );

4.4. Running the Application

Let's now focus on building all of our modules:

mvn clean package 

Then we run our application with the Java command without taking into account the provider:

java -cp ./exchange-rate-api/target/exchange-rate-api-1.0.0-SNAPSHOT.jar:./exchange-rate-app/target/exchange-rate-app-1.0.0-SNAPSHOT.jar com.baeldung.rate.app.MainApp

Now we'll include our provider in java.ext.dirs extension and we run the application again:

java -Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:./exchange-rate-impl/target:./exchange-rate-impl/target/depends -cp ./exchange-rate-api/target/exchange-rate-api-1.0.0-SNAPSHOT.jar:./exchange-rate-app/target/exchange-rate-app-1.0.0-SNAPSHOT.jar com.baeldung.rate.app.MainApp 

We can see that our provider is loaded.

5. Conclusion

Now that we have explored the Java SPI mechanism through well-defined steps, it should be clear to see how to use the Java SPI to create easily extensible or replaceable modules.

Although our example used the Yahoo exchange rate service to show the power of plugging-in to other existing external APIs, production systems don't need to rely on third-party APIs to create great SPI applications.

Kodnya, seperti biasa, terdapat di Github.