1. Pengenalan
Artikel ini akan mengkaji asas-asas Google Guice . Kami akan melihat pendekatan untuk menyelesaikan tugas Dependency Injection (DI) asas di Guice.
Kami juga akan membandingkan dan membezakan pendekatan Guice dengan kerangka DI yang lebih mapan seperti Spring and Contexts and Dependency Injection (CDI).
Artikel ini menganggap pembaca mempunyai pemahaman mengenai asas-asas corak Suntikan Ketergantungan.
2. Persediaan
Untuk menggunakan Google Guice dalam projek Maven anda, anda perlu menambahkan kebergantungan berikut ke pom.xml anda :
com.google.inject guice 4.1.0
Terdapat juga koleksi sambungan Guice (kami akan membahasnya sedikit kemudian) di sini, serta modul pihak ketiga untuk memperluas kemampuan Guice (terutamanya dengan menyediakan integrasi ke kerangka kerja Java yang lebih mapan).
3. Suntikan Ketergantungan Asas Dengan Guice
3.1. Permohonan Contoh Kami
Kami akan bekerja dengan senario di mana kami merancang kelas yang menyokong tiga cara komunikasi dalam perniagaan meja bantuan: E-mel, SMS, dan IM.
Pertimbangkan kelas:
public class Communication { @Inject private Logger logger; @Inject private Communicator communicator; public Communication(Boolean keepRecords) { if (keepRecords) { System.out.println("Message logging enabled"); } } public boolean sendMessage(String message) { return communicator.sendMessage(message); } }
Ini Komunikasi kelas adalah unit asas komunikasi. Contoh kelas ini digunakan untuk menghantar mesej melalui saluran komunikasi yang tersedia. Seperti yang ditunjukkan di atas, Komunikasi mempunyai Komunikator yang kita gunakan untuk melakukan penghantaran mesej yang sebenarnya.
Titik masuk asas ke Guice adalah Penyuntik:
public static void main(String[] args){ Injector injector = Guice.createInjector(new BasicModule()); Communication comms = injector.getInstance(Communication.class); }
Kaedah utama ini mengambil contoh kelas Komunikasi kami . Ia juga memperkenalkan konsep asas Guice: the Module (menggunakan BasicModule dalam contoh ini). The Modul adalah unit asas definisi pengikatan (atau pendawaian, kerana ia dikenali di Spring).
Guice telah menggunakan pendekatan kod pertama untuk suntikan dan pengurusan ketergantungan sehingga anda tidak akan menghadapi banyak XML yang tidak ada.
Dalam contoh di atas, pohon kebergantungan Komunikasi akan disuntik secara implisit menggunakan ciri yang disebut pengikatan tepat waktu , dengan syarat kelas mempunyai konstruktor default tanpa arg. Ini telah menjadi ciri di Guice sejak awal dan hanya tersedia di Spring sejak v4.3.
3.2. Ikatan Guice
Mengikat adalah Guice kerana pendawaian adalah ke Spring. Dengan penjilidan , anda menentukan bagaimana Guice akan memasukkan kebergantungan ke dalam kelas.
Pengikatan ditentukan dalam pelaksanaan com.google.inject.AbstractModule :
public class BasicModule extends AbstractModule { @Override protected void configure() { bind(Communicator.class).to(DefaultCommunicatorImpl.class); } }
Pelaksanaan modul ini menentukan bahawa contoh Default CommunicatorImpl akan disuntik di mana sahaja pemboleh ubah Communicator dijumpai.
Penjelmaan mekanisme ini yang lain adalah pengikatan yang dinamakan . Pertimbangkan pernyataan pemboleh ubah berikut:
@Inject @Named("DefaultCommunicator") Communicator communicator;
Untuk ini, kita akan mempunyai definisi mengikat berikut:
@Override protected void configure() { bind(Communicator.class) .annotatedWith(Names.named("DefaultCommunicator")) .to(Communicator.class); }
Pengikatan ini akan memberikan contoh Komunikator kepada pemboleh ubah yang diberi penjelasan dengan anotasi @Named (“DefaultCommunicator”) .
Anda akan notis itu @Inject dan @Named anotasi kelihatan anotasi pinjaman dari Jakarta EE CDI, dan mereka berada. Mereka berada dalam pakej com.google.inject. * - anda harus berhati-hati untuk mengimport dari pakej yang betul semasa menggunakan IDE.
Petua: Walaupun kami hanya berkata untuk menggunakan Guice yang disediakan @Inject dan @Named , ia berbaloi untuk ambil perhatian bahawa Guice tidak menyediakan sokongan untuk javax.inject.Inject dan javax.inject.Named, antara penjelasan Jakarta EE lain.
Anda juga boleh memasukkan kebergantungan yang tidak mempunyai konstruktor no-arg lalai menggunakan pengikat konstruktor :
public class BasicModule extends AbstractModule { @Override protected void configure() { bind(Boolean.class).toInstance(true); bind(Communication.class).toConstructor( Communication.class.getConstructor(Boolean.TYPE)); }
Coretan di atas akan menyuntikkan contoh Komunikasi menggunakan konstruktor yang mengambil argumen boolean . Kami membekalkan benar hujah untuk pembina oleh mentakrifkan tidak disasarkan mengikat daripada Boolean kelas.
Ini tidak disasarkan mengikat akan sabar dibekalkan kepada mana-mana pembina dalam mengikat yang menerima yang boolean parameter. Dengan pendekatan ini, semua ketergantungan Komunikasi disuntikkan.
Pendekatan lain untuk pengikatan khusus konstruktor adalah pengikatan instance , di mana kami memberikan contoh secara langsung dalam pengikatan:
public class BasicModule extends AbstractModule { @Override protected void configure() { bind(Communication.class) .toInstance(new Communication(true)); } }
Pengikatan ini akan memberikan contoh kelas Komunikasi di mana sahaja pemboleh ubah Komunikasi dinyatakan.
Walau bagaimanapun, dalam kes ini, pokok pergantungan kelas tidak akan disambung secara automatik. Anda harus mengehadkan penggunaan mod ini jika tidak diperlukan inisialisasi atau suntikan ketergantungan.
4. Jenis Suntikan Ketergantungan
Guice menyokong jenis suntikan standard yang mungkin anda jangkakan dengan corak DI. Di kelas Communicator , kita perlu menyuntik pelbagai jenis CommunicationMode .
4.1. Suntikan Medan
@Inject @Named("SMSComms") CommunicationMode smsComms;
Gunakan anotasi @Nama pilihan sebagai kelayakan untuk melaksanakan suntikan yang disasarkan berdasarkan nama
4.2. Kaedah Suntikan
Di sini kita menggunakan kaedah setter untuk mencapai suntikan:
@Inject public void setEmailCommunicator(@Named("EmailComms") CommunicationMode emailComms) { this.emailComms = emailComms; }
4.3. Suntikan Pembina
Anda juga boleh menyuntik kebergantungan menggunakan konstruktor:
@Inject public Communication(@Named("IMComms") CommunicationMode imComms) { this.imComms= imComms; }
4.4. Suntikan Tersirat
Guice will implicitly inject some general purpose components like the Injector and an instance of java.util.Logger, among others. You'll notice we are using loggers all through the samples but you won't find an actual binding for them.
5. Scoping in Guice
Guice supports the scopes and scoping mechanisms we have grown used to in other DI frameworks. Guice defaults to providing a new instance of a defined dependency.
5.1. Singleton
Let's inject a singleton into our application:
bind(Communicator.class).annotatedWith(Names.named("AnotherCommunicator")) .to(Communicator.class).in(Scopes.SINGLETON);
The in(Scopes.SINGLETON) specifies that any Communicator field with the @Named(“AnotherCommunicator”) will get a singleton injected. This singleton is lazily initiated by default.
5.2. Eager Singleton
Now, let's inject an eager singleton:
bind(Communicator.class).annotatedWith(Names.named("AnotherCommunicator")) .to(Communicator.class) .asEagerSingleton();
The asEagerSingleton() call defines the singleton as eagerly instantiated.
In addition to these two scopes, Guice supports custom scopes as well as the web-only @RequestScoped and @SessionScoped annotations, supplied by Jakarta EE (there are no Guice-supplied versions of those annotations).
6. Aspect Oriented Programming in Guice
Guice is compliant with the AOPAlliance's specifications for aspect-oriented programming. We can implement the quintessential logging interceptor, which we will use to track message sending in our example, in only four steps.
Step 1 – Implement the AOPAlliance's MethodInterceptor:
public class MessageLogger implements MethodInterceptor { @Inject Logger logger; @Override public Object invoke(MethodInvocation invocation) throws Throwable { Object[] objectArray = invocation.getArguments(); for (Object object : objectArray) { logger.info("Sending message: " + object.toString()); } return invocation.proceed(); } }
Step 2 – Define a Plain Java Annotation:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MessageSentLoggable { }
Step 3 – Define a Binding for a Matcher:
Matcher is a Guice class that we use do specify the components that our AOP annotation will apply to. In this case, we want the annotation to apply to implementations of CommunicationMode:
public class AOPModule extends AbstractModule { @Override protected void configure() { bindInterceptor( Matchers.any(), Matchers.annotatedWith(MessageSentLoggable.class), new MessageLogger() ); } }
We have specified a Matcher here that will apply our MessageLogger interceptor to any class, that has the MessageSentLoggable annotation applied to its methods.
Step 4 – Apply Our Annotation to Our Communicationmode and Load Our Module
@Override @MessageSentLoggable public boolean sendMessage(String message) { logger.info("SMS message sent"); return true; } public static void main(String[] args) { Injector injector = Guice.createInjector(new BasicModule(), new AOPModule()); Communication comms = injector.getInstance(Communication.class); }
7. Conclusion
Having looked at basic Guice functionality, we can see where the inspiration for Guice came from Spring.
Bersama dengan sokongannya untuk JSR-330, Guice bertujuan untuk menjadi kerangka DI yang berfokus pada suntikan (sedangkan Spring menyediakan keseluruhan ekosistem untuk kemudahan pengaturcaraan tidak semestinya hanya DI), yang disasarkan kepada pemaju yang menginginkan fleksibiliti DI.
Guice juga sangat luas, membolehkan pengaturcara menulis plugin mudah alih yang menghasilkan penggunaan kerangka yang fleksibel dan kreatif. Ini sebagai tambahan kepada integrasi luas yang sudah disediakan oleh Guice untuk kerangka kerja dan platform paling popular seperti Servlets, JSF, JPA, dan OSGi, untuk beberapa nama.
Anda boleh mendapatkan semua kod sumber yang digunakan dalam tutorial ini dalam projek GitHub kami.