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 !