Contents

AutoFactory 简介

1. 简介

在本教程中,我们将简要介绍来自 Google的AutoFactory

这是一个有助于生成工厂的源代码生成器。

2. Maven 设置

在开始之前,让我们在pom.xml 中添加以下依赖项:

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

最新版本可以在这里 找到。

3. 快速入门

现在让我们快速看一下AutoFactory可以做什么并创建一个简单的Phone类。

因此,当我们使用 @AutoFactory 注释Phone类并使用*@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适用于这个类的构造函数参数,这意味着注解的参数应该由注入的Provider 提供。

在上面的代码片段中,我们希望Phone由任何相机生产商提供,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));
    }
    
    // ...
}

现在我们有一个 由AutoFactory在编译时自动生成的PhoneFactory ,我们可以使用它来生成电话实例:

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

AutoFactory 将保留@Named 、*@Qualifier *以便我们可以使用它,例如,在使用依赖注入框架时:

@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. 非final工厂

请注意,生成的工厂类默认标记为 final,因此我们可以通过将allowSubclasses 属性设置为false 来更改此行为:

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

现在我们有:

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

4.3. 更多功能

此外,我们可以使用“implementing ”参数为生成的工厂指定一个接口列表来实现。

在这里,我们需要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;
    }
}

在这里,我们 使用extends 来扩展AbstractFactory 类。此外,我们应该注意,基础抽象类 (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. 带有Guice 的自动工厂

正如我们在本文前面提到的,AutoFactory支持JSR-330 注释 ,因此我们可以将现有的依赖注入框架与其集成。

首先,让我们将Guice 添加到pom.xml中:

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

可以在此处 找到最新版本的Guice

现在,我们将演示 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++));
    }
}

我们在SonyCameraModule中设置带有*@Named(“Sony”)注释的相机提供程序 以匹配PhoneFactory*的构造函数参数。

现在我们可以看到Guice正在为我们生成的工厂管理依赖注入:

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

6. 幕后花絮

AutoFactory提供的所有注解都是在编译阶段进行处理的,正如我们在文章中详细解释的:源码级注解 处理是如何工作的。