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