ServiceLoader: Kerangka DI built-in yang mungkin belum pernah Anda dengar

Salam, teman-teman. Jumat ini akan menjadi pelajaran pertama dalam grup kursus Java Developer yang baru. Ini untuk kursus ini bahwa publikasi saat ini akan dikhususkan.



Banyak pengembang java menggunakan Spring untuk mengimplementasikan dependensi. Beberapa mungkin telah mencoba Google Guice atau bahkan Layanan OSGi . Tetapi banyak yang tidak tahu bahwa Java sudah memiliki DI bawaan. Apakah Anda pikir itu muncul di Java 11 atau 12? Tidak, ini tersedia dengan Java 6.

ServiceLoader menyediakan kemampuan untuk mencari dan membuat instance antarmuka atau kelas abstrak. Jika Anda terbiasa dengan Spring, maka ini sangat mirip dengan penjelasan Bean dan Autowired . Mari kita lihat contoh penggunaan Spring dan ServiceLoader. Dan diskusikan persamaan dan perbedaannya.

Musim semi


Pertama, mari kita lihat cara membuat DI sederhana di Spring. Buat antarmuka sederhana:

public interface SimpleService { String echo(String value); } 

Dan implementasi antarmuka:

 import org.springframework.stereotype.Component; @Component public class SimpleServiceImpl implements SimpleService { public String echo(final String value) { return value; } } 

Lihat @Component . Anotasi ini akan mendaftarkan kelas kami sebagai kacang dalam konteks Musim Semi.

Dan kelas utama kami.

 @SpringBootApplication public class SpringExample implements CommandLineRunner { private static final Logger log = LoggerFactory.getLogger(SpringExample.class); @Autowired List<SimpleService> simpleServices; public static void main(String[] args) { SpringApplication.run(SpringExample.class, args); } public void run(final String... strings) throws Exception { for (SimpleService simpleService : simpleServices) { log.info("Echo: " + simpleService.echo(strings[0])); } } } 

Perhatikan anotasi @Autowired pada SimpleService kombo SimpleService . Annotation @SpringBootApplication dirancang untuk secara otomatis mencari kacang dalam sebuah paket. Kemudian saat startup, mereka secara otomatis disuntikkan ke SpringExample .

Serviceloader


Kami akan menggunakan antarmuka yang sama seperti pada contoh Musim Semi, jadi kami tidak akan mengulanginya di sini. Sebagai gantinya, segera lihat implementasi layanan:

 import com.google.auto.service.AutoService; @AutoService(SimpleService.class) public class SimpleServiceImpl implements SimpleService { public String echo(final String value) { return value; } } 

Dalam implementasinya, kami โ€œmendaftarkanโ€ turunan layanan menggunakan anotasi @AutoService . Anotasi ini diperlukan hanya pada waktu kompilasi, karena javac menggunakannya untuk secara otomatis menghasilkan file registrasi layanan ( Catatan Penerjemah: untuk ketergantungan maven yang mengandung @AutoService , tentukan cakupan - disediakan) :

META-INF/services/io.github.efenglu.serviceLoader.example.SimpleService

File ini berisi daftar kelas yang mengimplementasikan layanan:

io.github.efenglu.serviceLoader.example.SimpleServiceImpl

Nama file harus nama lengkap layanan (antarmuka). Suatu file dapat memiliki sejumlah implementasi, masing-masing pada baris yang terpisah.

Dalam implementasi, HARUS ada konstruktor tanpa parameter. Anda dapat membuat file seperti itu secara manual, tetapi menggunakan anotasi jauh lebih mudah. Dan kelas utama:

 public class ServiceLoaderExample { public static void main(String [] args) { final ServiceLoader<SimpleService> services = ServiceLoader.load(SimpleService.class); for (SimpleService service : services) { System.out.println("Echo: " + service.echo(args[0])); } } } 

Metode ServiceLoader.load dipanggil untuk mendapatkan ServiceLoader , yang dapat digunakan untuk mendapatkan instance layanan. Contoh ServiceLoader mengimplementasikan antarmuka Iterable untuk jenis layanan, oleh karena itu, variabel services dapat digunakan di for each loop.

Jadi apa


Kedua metode ini relatif kecil. Keduanya dapat digunakan dengan anotasi dan karenanya cukup mudah digunakan. Jadi mengapa menggunakan ServiceLoader alih-alih Spring?

Ketergantungan


Mari kita lihat pohon ketergantungan contoh Musim Semi sederhana kami:

 [INFO] -----------< io.github.efenglu.serviceLoader:spring-example >----------- [INFO] Building spring-example 1.0.X-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-dependency-plugin:3.1.1:tree (default-cli) @ spring-example --- [INFO] io.github.efenglu.serviceLoader:spring-example:jar:1.0.X-SNAPSHOT [INFO] +- org.slf4j:slf4j-api:jar:1.7.25:compile [INFO] +- org.springframework:spring-context:jar:4.3.22.RELEASE:compile [INFO] | +- org.springframework:spring-aop:jar:4.3.22.RELEASE:compile [INFO] | +- org.springframework:spring-core:jar:4.3.22.RELEASE:compile [INFO] | | \- commons-logging:commons-logging:jar:1.2:compile [INFO] | \- org.springframework:spring-expression:jar:4.3.22.RELEASE:compile [INFO] +- org.springframework.boot:spring-boot-autoconfigure:jar:1.5.19.RELEASE:compile [INFO] +- org.springframework.boot:spring-boot:jar:1.5.19.RELEASE:compile [INFO] \- org.springframework:spring-beans:jar:4.3.22.RELEASE:compile 

Dan bandingkan dengan ServiceLoader:

 [INFO] io.github.efenglu.serviceLoader:serviceLoader-example:jar:1.0.X-SNAPSHOT ## Only provided dependencies for the auto service annotation [INFO] \- com.google.auto.service:auto-service:jar:1.0-rc4:provided [INFO] +- com.google.auto:auto-common:jar:0.8:provided [INFO] \- com.google.guava:guava:jar:23.5-jre:provided [INFO] +- com.google.code.findbugs:jsr305:jar:1.3.9:provided [INFO] +- org.checkerframework:checker-qual:jar:2.0.0:provided [INFO] +- com.google.errorprone:error_prone_annotations:jar:2.0.18:provided [INFO] +- com.google.j2objc:j2objc-annotations:jar:1.1:provided [INFO] \- org.codehaus.mojo:animal-sniffer-annotations:jar:1.14:provided 

Jika kami tidak memperhatikan dependensi yang disediakan, maka ServiceLoader tidak memiliki dependensi. Itu benar, dia hanya butuh Jawa.

Tidak masalah jika Anda mengembangkan aplikasi berbasis Musim Semi, tetapi jika Anda menulis sesuatu yang akan digunakan dalam banyak kerangka kerja yang berbeda atau jika Anda memiliki aplikasi konsol kecil, ini sudah dapat menjadi sangat penting.

Kecepatan


Untuk aplikasi konsol, waktu startup ServiceLoader jauh lebih pendek daripada Spring Boot App. Ini karena semakin sedikit jumlah kode yang dapat diunduh, tidak ada pemindaian, tidak adanya refleksi, tidak adanya kerangka kerja yang besar.

Memori


Musim semi tidak terkenal karena menghemat memori. Jika penggunaan memori penting bagi Anda, maka pertimbangkan untuk menggunakan ServiceLoader untuk DI.

Modul Java


Salah satu aspek kunci dari modul Java adalah kemampuan untuk sepenuhnya melindungi kelas dalam suatu modul dari kode di luar modul. ServiceLoader adalah mekanisme yang memungkinkan kode eksternal untuk "mengakses" implementasi internal. Modul Java memungkinkan Anda untuk mendaftar layanan untuk implementasi internal, sambil menjaga perbatasan.

Bahkan, ini adalah satu-satunya mekanisme dukungan injeksi ketergantungan yang disetujui secara resmi untuk modul Java. Pegas dan sebagian besar kerangka kerja DI lainnya menggunakan refleksi untuk menemukan dan menghubungkan komponen-komponennya. Tetapi ini tidak kompatibel dengan modul Java. Bahkan refleksi tidak dapat melihat ke dalam modul (kecuali jika Anda mengizinkannya, tetapi mengapa Anda harus mengizinkannya).

Kesimpulan


Musim semi adalah hal yang hebat. Ini memiliki lebih banyak fungsi daripada yang pernah ada di ServiceLoader. Tetapi ada kalanya ServiceLoader adalah pilihan yang tepat. Sederhana, kecil, cepat, dan selalu tersedia.

Kode sumber lengkap untuk contoh di Git Repo saya.

Itu saja. Sampai jumpa di lapangan !

Source: https://habr.com/ru/post/id457440/


All Articles