Antarmuka Penyedia Layanan Java

1. Ikhtisar

Java 6 telah memperkenalkan fitur untuk menemukan dan memuat implementasi yang cocok dengan antarmuka yang diberikan: Service Provider Interface (SPI).

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

2. Istilah dan Definisi SPI Java

Java SPI mendefinisikan empat komponen utama

2.1. Layanan

Kumpulan antarmuka dan kelas pemrograman terkenal yang menyediakan akses ke beberapa fungsi atau fitur aplikasi tertentu.

2.2. Antarmuka Penyedia Layanan

Antarmuka atau kelas abstrak yang bertindak sebagai proxy atau titik akhir ke layanan.

Jika layanan adalah satu antarmuka, maka itu sama dengan antarmuka penyedia layanan.

Layanan dan SPI bersama-sama terkenal di Ekosistem Jawa sebagai API.

2.3. Penyedia layanan

Implementasi khusus dari SPI. Penyedia Layanan berisi satu atau beberapa kelas konkret yang mengimplementasikan atau memperluas jenis layanan.

Penyedia Layanan dikonfigurasi dan diidentifikasi melalui file konfigurasi penyedia yang kami masukkan ke direktori sumber daya META-INF / services . Nama file adalah nama SPI yang memenuhi syarat dan isinya adalah nama yang sepenuhnya memenuhi syarat untuk implementasi SPI.

Penyedia Layanan dipasang dalam bentuk ekstensi, file jar yang kami tempatkan di jalur kelas aplikasi, jalur kelas ekstensi Java, atau jalur kelas yang ditentukan pengguna.

2.4. ServiceLoader

Inti dari SPI adalah kelas ServiceLoader . Ini memiliki peran untuk menemukan dan memuat implementasi dengan malas. Ini menggunakan jalur kelas konteks untuk menemukan implementasi penyedia dan meletakkannya di cache internal.

3. Sampel SPI di Ekosistem Jawa

Java menyediakan banyak SPI. Berikut beberapa contoh antarmuka penyedia layanan dan layanan yang disediakannya:

  • CurrencyNameProvider: menyediakan simbol mata uang lokal untuk kelas Mata Uang .
  • LocaleNameProvider: menyediakan nama yang dilokalkan untuk kelas Lokal .
  • TimeZoneNameProvider: memberikan nama zona waktu yang dilokalkan untuk kelas TimeZone .
  • DateFormatProvider: menyediakan format tanggal dan waktu untuk lokal tertentu.
  • NumberFormatProvider: memberikan nilai moneter, integer, dan persentase untuk kelas NumberFormat .
  • Driver: pada versi 4.0, JDBC API mendukung pola SPI. Versi yang lebih lama menggunakan metode Class.forName () untuk memuat driver.
  • PersistenceProvider: menyediakan implementasi JPA API.
  • JsonProvider: menyediakan objek pemrosesan JSON.
  • JsonbProvider: menyediakan objek pengikatan JSON.
  • Ekstensi: menyediakan ekstensi untuk wadah CDI.
  • ConfigSourceProvider : menyediakan sumber untuk mengambil properti konfigurasi.

4. Showcase: Aplikasi Nilai Tukar Mata Uang

Sekarang setelah kita memahami dasar-dasarnya, mari kita jelaskan langkah-langkah yang diperlukan untuk menyiapkan aplikasi nilai tukar.

Untuk menyoroti langkah-langkah ini, kita perlu menggunakan setidaknya tiga proyek: exchange-rate-api , exchange-rate-impl, dan exchange-rate-app.

Di sub-bagian 4.1., Kita akan membahas Service , SPI dan ServiceLoader melalui modul exchange-rate-api, kemudian di sub-bagian 4.2. kita akan mengimplementasikan penyedia layanan kita dalam modul exchange-rate-impl , dan terakhir, kita akan menyatukan semuanya di sub-bagian 4.3 melalui modul exchange-rate-app .

Bahkan, kami dapat menyediakan sebanyak modul yang kita butuhkan untuk se rvice penyedia dan membuat mereka tersedia dalam classpath modul tukar-aplikasi.

4.1. Membangun API Kami

Kami mulai dengan membuat proyek Maven yang disebut exchange-rate-api . Praktik yang baik bahwa nama diakhiri dengan istilah api , tetapi kita bisa menyebutnya apa saja.

Kemudian kami membuat kelas model untuk mewakili mata uang kurs:

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

Dan kemudian kami mendefinisikan Layanan kami untuk mengambil kutipan dengan membuat antarmuka QuoteManager:

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

Selanjutnya, kami membuat SPI untuk layanan kami:

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

Dan terakhir, kita perlu membuat kelas utilitas ExchangeRate.java yang dapat digunakan oleh kode klien. Kelas ini mendelegasikan ke ServiceLoader .

Pertama, kita memanggil load () metode pabrik statis untuk mendapatkan instance ServiceLoader:

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

Dan kemudian kita memanggil metode iterate () untuk mencari dan mengambil semua implementasi yang tersedia.

Iterator = loader.iterator(); 

Hasil pencarian di-cache sehingga kita dapat menjalankan metode ServiceLoader.reload () untuk menemukan implementasi yang baru diinstal:

Iterator = loader.reload(); 

Dan inilah kelas utilitas kami:

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

Sekarang kita memiliki layanan untuk mendapatkan semua implementasi yang terinstal, kita dapat menggunakan semuanya dalam kode klien kita untuk memperluas aplikasi kita atau hanya satu 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.

Kode tersebut, seperti biasa, dapat ditemukan di Github.