Pengenalan AutoFactory

1. Pengenalan

Dalam tutorial ini, kami akan memberikan pengenalan ringkas kepada AutoFactory , dari Google.

Ini adalah penjana kod peringkat sumber yang membantu menjana kilang.

2. Persediaan Maven

Sebelum memulakan, mari tambahkan kebergantungan berikut ke pom.xml:

 com.google.auto.factory auto-factory 1.0-beta5 

Versi terbaru boleh didapati di sini.

3. Mulakan Pantas

Sekarang mari kita lihat apa yang boleh dilakukan oleh AutoFactory dan membuat kelas Telefon yang ringkas .

Oleh itu, apabila kami memberi penjelasan pada kelas Telefon dengan @AutoFactory dan parameter pembangunnya dengan @Provided , kami mendapat:

@AutoFactory public class Phone { private final Camera camera; private final String otherParts; PhoneAssembler(@Provided Camera camera, String otherParts) { this.camera = camera; this.otherParts = otherParts; } //... }

Kami hanya menggunakan dua anotasi: @AutoFactory dan @Provided . Apabila kita memerlukan kilang yang dihasilkan untuk kelas kita, kita dapat memberi anotasinya dengan @AutoFactory, sedangkan @Provided berlaku untuk parameter konstruktor kelas ini, dan ini bermaksud bahawa parameter yang dijelaskan harus disediakan oleh Penyedia yang disuntikkan .

Dalam coretan di atas, kami mengharapkan Kamera disediakan oleh mana-mana pengeluar kamera dan AutoFactory akan membantu menghasilkan kod berikut:

@Generated("com.google.auto.factory.processor.AutoFactoryProcessor") public final class PhoneFactory { private final Provider cameraProvider; @Inject PhoneAssemblerFactory(Provider cameraProvider) { this.cameraProvider = checkNotNull(cameraProvider, 1); } PhoneAssembler create(String otherParts) { return new PhoneAssembler( checkNotNull(cameraProvider.get(), 1), checkNotNull(otherParts, 2)); } // ... }

Sekarang kita mempunyai PhoneFactory yang dihasilkan secara automatik oleh AutoFactory pada waktu kompilasi, dan kita dapat menggunakannya untuk menghasilkan contoh telefon:

PhoneFactory phoneFactory = new PhoneFactory( () -> new Camera("Unknown", "XXX")); Phone simplePhone = phoneFactory.create("other parts");

The @AutoFactory anotasi boleh digunakan untuk pengeluar dan juga:

public class ClassicPhone { private final String dialpad; private final String ringer; private String otherParts; @AutoFactory public ClassicPhone( @Provided String dialpad, @Provided String ringer) { this.dialpad = dialpad; this.ringer = ringer; } @AutoFactory public ClassicPhone(String otherParts) { this("defaultDialPad", "defaultRinger"); this.otherParts = otherParts; } //... }

Dalam coretan di atas, kami menggunakan @AutoFactory untuk kedua-dua pembina. AutoFactory hanya akan menghasilkan dua kaedah penciptaan untuk kita dengan sewajarnya:

@Generated(value = "com.google.auto.factory.processor.AutoFactoryProcessor") public final class ClassicPhoneFactory { private final Provider java_lang_StringProvider; @Inject public ClassicPhoneFactory(Provider java_lang_StringProvider) { this.java_lang_StringProvider = checkNotNull(java_lang_StringProvider, 1); } public ClassicPhone create() { return new ClassicPhone( checkNotNull(java_lang_StringProvider.get(), 1), checkNotNull(java_lang_StringProvider.get(), 2)); } public ClassicPhone create(String otherParts) { return new ClassicPhone(checkNotNull(otherParts, 1)); } //... }

AutoFactory juga menyokong parameter yang dijelaskan dengan @Provided , tetapi hanya untuk anotasi JSR-330.

Sebagai contoh, jika kita mahu kameraProvider menjadi "Sony", kita dapat mengubah kelas Telefon menjadi:

@AutoFactory public class Phone { PhoneAssembler( @Provided @Named("Sony") Camera camera, String otherParts) { this.camera = camera; this.otherParts = otherParts; } //... }

AutoFactory akan mengekalkan @Named @Qualifier sehingga kita dapat menggunakannya, sebagai contoh, ketika menggunakan kerangka Dependency Injection:

@Generated("com.google.auto.factory.processor.AutoFactoryProcessor") public final class PhoneFactory { private final Provider cameraProvider; @Inject PhoneAssemblerFactory(@Named("Sony") Provider cameraProvider) { this.cameraProvider = checkNotNull(cameraProvider, 1); } //... }

4. Penjanaan Kod yang disesuaikan

Terdapat beberapa atribut yang dapat kita gunakan dengan anotasi @AutoFactory untuk menyesuaikan kod yang dihasilkan.

4.1. Nama Kelas Tersuai

Nama kelas kilang yang dihasilkan boleh ditetapkan dengan className :

@AutoFactory(className = "SamsungFactory") public class SmartPhone { //... }

Dengan konfigurasi di atas, kami akan membuat kelas bernama SamsungFactory :

@Generated("com.google.auto.factory.processor.AutoFactoryProcessor") public final class SamsungFactory { //... }

4.2. Kilang tidak muktamad

Perhatikan bahawa kelas kilang yang dihasilkan ditandakan sebagai akhir secara lalai, jadi kami dapat mengubah tingkah laku ini dengan menetapkan atribut allowSubclasses ke false:

@AutoFactory( className = "SamsungFactory", allowSubclasses = true) public class SmartPhone { //... }

Sekarang kita mempunyai:

@Generated("com.google.auto.factory.processor.AutoFactoryProcessor") public class SamsungFactory { //... }

4.3. Lebih Berkebolehan

Selain itu, kita dapat menentukan senarai antara muka untuk kilang yang dihasilkan untuk dilaksanakan menggunakan parameter "implementasi".

Di sini kita memerlukan SamsungFactory untuk menghasilkan telefon pintar dengan storan yang boleh disesuaikan:

public interface CustomStorage { SmartPhone customROMInGB(int romSize); }

Perhatikan bahawa kaedah dalam antara muka harus mengembalikan contoh SmartPhone kelas asas .

Kemudian, untuk menghasilkan kelas kilang dengan antara muka yang dilaksanakan di atas, AutoFactory memerlukan pembangun yang relevan di kelas asas :

@AutoFactory( className = "SamsungFactory", allowSubclasses = true, implementing = CustomStorage.class) public class SmartPhone { public SmartPhone(int romSize){ //... } //... }

Oleh itu, AutoFactory akan menghasilkan kod berikut:

@Generated("com.google.auto.factory.processor.AutoFactoryProcessor") public class SamsungFactory implements CustomStorage { //... public SmartPhone create(int romSize) { return new SmartPhone(romSize); } @Override public SmartPhone customROMInGB(int romSize) { return create(romSize); } }

4.4. Kilang Dengan Sambungan

Oleh kerana AutoFactory dapat menghasilkan implementasi antara muka, adalah wajar untuk mengharapkannya dapat memperpanjang kelas juga dan ini memang mungkin:

public abstract class AbstractFactory { abstract CustomPhone newInstance(String brand); } @AutoFactory(extending = AbstractFactory.class) public class CustomPhone { private final String brand; public CustomPhone(String brand) { this.brand = brand; } }

Here, we extended the AbstractFactory class using extending. Also, we should note that each abstract method in the base abstract class (AbstractFactory) should have a corresponding constructor in the concrete class (CustomPhone).

Finally, we can see the following generated code:

@Generated("com.google.auto.factory.processor.AutoFactoryProcessor") public final class CustomPhoneFactory extends AbstractFactory { @Inject public CustomPhoneFactory() { } public CustomPhone create(String brand) { return new CustomPhone(checkNotNull(brand, 1)); } @Override public CustomPhone newInstance(String brand) { return create(brand); } //... }

We can see that AutoFactory is smart enough to make use of the constructor to implement the corresponding abstract method – great features like this in AutoFactory surely will save us lots of time and code.

5. AutoFactory With Guice

As we mentioned earlier in this article, AutoFactory supports JSR-330 annotations, so we can integrate existing dependency injection framework with it.

First, let's add Guice to the pom.xml:

 com.google.inject guice 4.2.0 

The latest version of Guice can be found here.

Now, we'll demonstrate how well AutoFactory integrates with Guice.

As we expect “Sony” to be the camera provider, we need to inject a SonyCameraProvider to PhoneFactory‘s constructor:

@Generated("com.google.auto.factory.processor.AutoFactoryProcessor") public final class PhoneFactory { private final Provider cameraProvider; @Inject public PhoneFactory(@Named("Sony") Provider cameraProvider) { this.cameraProvider = checkNotNull(cameraProvider, 1); } //... }

Finally, we'll make the binding in a Guice module:

public class SonyCameraModule extends AbstractModule { private static int SONY_CAMERA_SERIAL = 1; @Named("Sony") @Provides Camera cameraProvider() { return new Camera( "Sony", String.format("%03d", SONY_CAMERA_SERIAL++)); } }

And we set the camera provider annotated with @Named(“Sony”) in SonyCameraModule to match PhoneFactory‘s constructor parameter.

Now we can see that Guice is managing dependency injection for our generated factory:

Injector injector = Guice.createInjector(new SonyCameraModule()); PhoneFactory injectedFactory = injector.getInstance(PhoneFactory.class); Phone xperia = injectedFactory.create("Xperia");

6. Under the Hood

All annotations provided by AutoFactory are processed in the compilation stage, as we have explained in detail in the article: how the source-level annotation processing works.

7. Conclusion

Dalam artikel ini, kami telah memperkenalkan cara menggunakan AutoFactory, dan bagaimana mengintegrasikannya dengan Guice - kilang menulis boleh berulang dan ralat - alat penjanaan kod seperti AutoFactory dan AutoValue dapat menjimatkan banyak masa dan membebaskan kita dari bug halus .

Seperti biasa, pelaksanaan sampel kod lengkap boleh didapati di Github.