1. Введение
В этом учебнике, мы дадим краткое введение в АвтоФактория , от Google.
Это генератор кода на уровне источника, который помогает создавать заводы.
2. Мавен Настройка
Прежде чем мы начнем, давайте добавим следующую зависимость к пом.xml:
com.google.auto.factory auto-factory 1.0-beta5
Последнюю версию можно найти здесь .
3. Быстрый старт
Давайте сейчас посмотрим, что АвтоФактория может сделать и создать простой Телефонная класс.
Итак, когда мы аннотировать Телефонная класс с @AutoFactory и параметр конструктора с @Provided , мы получаем:
@AutoFactory public class Phone { private final Camera camera; private final String otherParts; PhoneAssembler(@Provided Camera camera, String otherParts) { this.camera = camera; this.otherParts = otherParts; } //... }
Мы использовали только две аннотации: @AutoFactory и @Provided . Когда нам нужна фабрика, созданная для нашего класса, мы можем аннотировать ее @AutoFactory, в то @Provided относится к параметрам конструктора этого класса, а это означает, что аннотированный параметр должен быть предоставлен инъекционным Поставщик .
В фрагменте выше, мы ожидаем, что Оператор быть предоставлены любым производителем камеры и АвтоФактория поможет создать следующий код:
@Generated("com.google.auto.factory.processor.AutoFactoryProcessor") public final class PhoneFactory { private final ProvidercameraProvider; @Inject PhoneAssemblerFactory(Provider cameraProvider) { this.cameraProvider = checkNotNull(cameraProvider, 1); } PhoneAssembler create(String otherParts) { return new PhoneAssembler( checkNotNull(cameraProvider.get(), 1), checkNotNull(otherParts, 2)); } // ... }
Теперь у нас есть ТелефонФактория генерируется автоматически АвтоФактория во время компиляции, и мы можем использовать его для создания экземпляров телефона:
PhoneFactory phoneFactory = new PhoneFactory( () -> new Camera("Unknown", "XXX")); Phone simplePhone = phoneFactory.create("other parts");
@AutoFactory аннотация может быть применена и к конструкторам:
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; } //... }
В фрагменте выше, мы применили @AutoFactory обоим конструкторам. АвтоФактория будет просто генерировать два метода создания для нас соответственно:
@Generated(value = "com.google.auto.factory.processor.AutoFactoryProcessor") public final class ClassicPhoneFactory { private final Providerjava_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)); } //... }
Автофактория также поддерживает параметры, аннотированные @Provided , но только для JSR-330 Аннотации.
Например, если мы хотим камераПровид быть “Sony”, мы можем изменить Телефонная от класса до:
@AutoFactory public class Phone { PhoneAssembler( @Provided @Named("Sony") Camera camera, String otherParts) { this.camera = camera; this.otherParts = otherParts; } //... }
AutoFactory сохранит @Named @Qualifier так что мы можем использовать его, например, при использовании фреймворка инъекций зависимости:
@Generated("com.google.auto.factory.processor.AutoFactoryProcessor") public final class PhoneFactory { private final ProvidercameraProvider; @Inject PhoneAssemblerFactory(@Named("Sony") Provider cameraProvider) { this.cameraProvider = checkNotNull(cameraProvider, 1); } //... }
4. Индивидуальное поколение кода
Есть несколько атрибутов, которые мы можем использовать с @AutoFactory аннотация для настройки сгенерированного кода.
4.1. Название пользовательского класса
Название сгенерированного заводского класса можно установить с помощью className :
@AutoFactory(className = "SamsungFactory") public class SmartPhone { //... }
С конфигурацией выше, мы создадим класс под названием SamsungFactory :
@Generated("com.google.auto.factory.processor.AutoFactoryProcessor") public final class SamsungFactory { //... }
4.2. Неокордные фабрики
Обратите внимание, что генерируемый класс фабрики по умолчанию помечается как окончательный, поэтому мы можем изменить это поведение, установив позволяютСубклассы атрибут к ложный:
@AutoFactory( className = "SamsungFactory", allowSubclasses = true) public class SmartPhone { //... }
Теперь у нас есть:
@Generated("com.google.auto.factory.processor.AutoFactoryProcessor") public class SamsungFactory { //... }
4.3. Больше возможностей
Кроме того, мы можем указать список интерфейсов для генерируемого завода для реализации с помощью “ реализации” параметр.
Здесь нам нужна SamsungFactory для производства смартфонов с настраиваемым хранилищем:
public interface CustomStorage { SmartPhone customROMInGB(int romSize); }
Обратите внимание, что методы в интерфейсе должны возвращать экземпляры базового класса смартфон .
Затем, чтобы создать заводской класс с интерфейсом выше реализованы, АвтоФактория требуется соответствующие конструкторы в базовом классе :
@AutoFactory( className = "SamsungFactory", allowSubclasses = true, implementing = CustomStorage.class) public class SmartPhone { public SmartPhone(int romSize){ //... } //... }
Таким образом, АвтоФактория будет генерировать следующий код:
@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. Фабрики с расширениями
С АвтоФактория может генерировать реализации интерфейса, естественно ожидать, что он сможет расширить классы, а также, и это возможно действительно:
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; } }
Здесь мы продлили АбстрактФактория класс с использованием расширение . Кроме того, мы должны обратите внимание, что каждый абстрактный метод в базовом абстрактном классе ( АбстрактФактория ) должен иметь соответствующего конструктора в конкретном классе ( Пользовательские телефоны ) .
Наконец, мы можем увидеть следующий генерируемый код:
@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); } //... }
Мы видим, что АвтоФактория достаточно умен, чтобы использовать конструктора для реализации соответствующего абстрактного метода – большие возможности, как это АвтоФактория безусловно, сэкономит нам много времени и кода.
5. Автофабрика с Guice
Как мы упоминали ранее в этой статье, АвтоФактория поддерживает Аннотации JSR-330 , таким образом, мы можем интегрировать существующую инфраструктуру впрыска зависимости с ней.
Во-первых, давайте добавим Гойс в пом.xml :
com.google.inject guice 4.2.0
Последняя версия Гойс можно найти здесь .
Теперь мы продемонстрируем, насколько хорошо АвтоФактория интегрируется с Гойс .
Как мы ожидаем, “Sony”, чтобы быть поставщиком камеры, мы должны ввести SonyCameraПровидятель ТелефонФактория Конструктор:
@Generated("com.google.auto.factory.processor.AutoFactoryProcessor") public final class PhoneFactory { private final ProvidercameraProvider; @Inject public PhoneFactory(@Named("Sony") Provider cameraProvider) { this.cameraProvider = checkNotNull(cameraProvider, 1); } //... }
Наконец, мы сделаем привязку в Гойс модуль:
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++)); } }
И мы установили камеру провайдера аннотированный с @Named (“Сони”) в SonyCameraModule чтобы соответствовать ТелефонФактория Параметр конструктора.
Теперь мы видим, что Гойс управляет инъекцией зависимости для нашего генерируемого завода:
Injector injector = Guice.createInjector(new SonyCameraModule()); PhoneFactory injectedFactory = injector.getInstance(PhoneFactory.class); Phone xperia = injectedFactory.create("Xperia");
6. Под капотом
Все аннотации, предоставленные АвтоФактория обрабатываются на этапе компиляции , как мы подробно объяснили в статье: как работает обработка аннотации на уровне источника.
7. Заключение
В этой статье мы представили, как использовать AutoFactory, и как интегрировать его с Гойс — фабрики по написанию могут быть повторяющимися и подверженными ошибкам инструментами генерации кода, такими как АвтоФактория и Автовалю может сэкономить нам много времени и освободить нас от тонких ошибок.
Как всегда, полную реализацию образцов кода можно найти более на Github .