Введение в Автозавод

1. Вступление

В этом руководстве мы кратко познакомимся с AutoFactory от Google.

Это генератор исходного кода, который помогает генерировать фабрики.

2. Maven Setup

Прежде чем мы начнем, давайте добавим следующую зависимость в pom.xml:

<dependency>
    <groupId>com.google.auto.factory</groupId>
    <artifactId>auto-factory</artifactId>
    <version>1.0-beta5</version>
</dependency>

Самую последнюю версию можно найти Вот .

3. Быстрый старт

Теперь давайте кратко рассмотрим, что может делать AutoFactory , и создадим простой класс Phone

Итак, когда мы аннотируем класс Phone с помощью @ 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 применяется к параметрам конструктора этого класса, и это означает, что аннотированный параметр должен предоставляться введенным https://docs . .oracle.com/JavaEE/6/API/javax/впрыснуть/Provider.html[ Provider ].

В приведенном выше фрагменте мы ожидаем, что Camera будет предоставлен любым производителем камеры, а AutoFactory поможет сгенерировать следующий код:

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

    private final Provider<Camera> cameraProvider;

    @Inject
    PhoneAssemblerFactory(Provider<Camera> cameraProvider) {
        this.cameraProvider = checkNotNull(cameraProvider, 1);
    }

    PhoneAssembler create(String otherParts) {
      return new PhoneAssembler(
        checkNotNull(cameraProvider.get(), 1),
        checkNotNull(otherParts, 2));
    }

   //...

}

Теперь у нас есть PhoneFactory , автоматически сгенерированный AutoFactory во время компиляции, и мы можем использовать его для создания телефонных экземпляров:

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 к обоим конструкторам.

AutoFactory просто сгенерирует два метода создания для нас соответственно:

@Generated(value = "com.google.auto.factory.processor.AutoFactoryProcessor")
public final class ClassicPhoneFactory {
    private final Provider<String> java__lang__StringProvider;

    @Inject
    public ClassicPhoneFactory(Provider<String> 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 также поддерживает параметры, отмеченные @ Provided , но только для аннотаций JSR-330 . **

Например, если мы хотим, чтобы cameraProvider был «Sony», мы можем изменить класс Phone на:

@AutoFactory
public class Phone {

    PhoneAssembler(
      @Provided @Named("Sony") Camera camera, String otherParts) {
        this.camera = camera;
        this.otherParts = otherParts;
    }

   //...

}

Автозавод сохранит https://docs.oracle.com/javaee/6/api/javax/inject/Named.html @Qualifier , чтобы мы могли использовать его, например, при использовании каркасов Dependency Injection:

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

    private final Provider<Camera> cameraProvider;

    @Inject
    PhoneAssemblerFactory(@Named("Sony") Provider<Camera> 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. non-final Фабрики

Обратите внимание, что сгенерированный класс фабрики по умолчанию помечен как окончательный, поэтому мы можем изменить это поведение, установив allowSubclasses атрибут false:

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

   //...

}

Теперь у нас есть:

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

   //...

}

4.3. Больше возможностей

Кроме того, мы можем указать список интерфейсов для создаваемой фабрики, используя https://github.com/google/auto/blob/master/factory/src/main/java/com/google/auto/factory/AutoFactory .java # L53 _https://github.com/google/auto/blob/master/factory/src/main/java/com/google/auto/factory/AutoFactory.java#L53[внедряющий]]параметр. _

Здесь нам нужен SamsungFactory для производства смартфонов с настраиваемым хранилищем:

public interface CustomStorage {
    SmartPhone customROMInGB(int romSize);
}
  • Обратите внимание, что методы в интерфейсе должны возвращать экземпляры базового класса SmartPhone . **

  • Затем, чтобы сгенерировать фабричный класс с реализованным выше интерфейсом, AutoFactory требует соответствующих конструкторов в базовом классе ** :

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

    public SmartPhone(int romSize){
       //...
    }

   //...

}

Таким образом, AutoFactory сгенерирует следующий код:

@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. Заводы с расширениями

Поскольку AutoFactory может генерировать реализации интерфейса, естественно ожидать, что он также сможет расширять классы, и это действительно возможно:

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;
    }
}

Здесь мы расширили класс AbstractFactory с помощью extending .

Кроме того, мы должны отметить, что каждый абстрактный метод в базовом абстрактном классе ( AbstractFactory ) должен иметь соответствующий конструктор в конкретном классе ( CustomPhone ) .

Наконец, мы можем увидеть следующий сгенерированный код:

@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);
    }

   //...

}

Мы можем видеть, что AutoFactory достаточно умен, чтобы использовать конструктор для реализации соответствующего абстрактного метода - такие замечательные функции в AutoFactory наверняка сэкономят нам много времени и кода.

5. AutoFactory с Guice

Как мы упоминали ранее в этой статье, AutoFactory поддерживает JSR-330 аннотации , поэтому мы можем интегрировать существующую инфраструктуру внедрения зависимостей с этим.

Сначала добавим Guice в pom.xml :

<dependency>
    <groupId>com.google.inject</groupId>
    <artifactId>guice</artifactId>
    <version>4.2.0</version>
</dependency>

Последнюю версию Guice можно найти по адресу here[here .

Теперь мы продемонстрируем, насколько хорошо AutoFactory интегрируется с Guice .

Поскольку мы ожидаем, что «Sony» станет поставщиком камер, нам нужно добавить SonyCameraProvider в конструктор PhoneFactory ‘:

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

    private final Provider<Camera> cameraProvider;

    @Inject
    public PhoneFactory(@Named("Sony") Provider<Camera> cameraProvider) {
        this.cameraProvider = checkNotNull(cameraProvider, 1);
    }

   //...

}

Наконец, мы сделаем привязку в модуле Guice :

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 («Sony») в SonyCameraModule , для соответствия параметру конструктора PhoneFactory ‘.

Теперь мы можем видеть, что Guice управляет внедрением зависимостей для нашей сгенерированной фабрики:

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

6. Под капотом

  • Все аннотации, предоставленные AutoFactory , обрабатываются на этапе компиляции ** , как мы подробно объясняли в статье:

ссылка:/java-annotation-processing-builder[как работает обработка аннотаций на уровне источника.]

7. Заключение

В этой статье мы познакомились с тем, как использовать AutoFactory и как его интегрировать с фабриками написания Guice – . Это может быть повторяющимся и подверженным ошибкам - такие инструменты генерации кода, как AutoFactory и link:/вводная-в-автоматическую ценность[ AutoValue ]могут сэкономить нам много времени и освободить нас от тонких ошибок.

Как всегда, полную реализацию примеров кода можно найти over на Github .