25

来自匕首讨论@

我有一个类,它从对象图中获取一些依赖关系,并在运行时从调用者那里获取其他依赖关系。

public class ImageDownloader {
  // Get these dependencies from the injector.
  private final HttpClient httpClient;
  private final ExecutorService executorService;

  // Get these from the caller.
  private final URL imageUrl;
  private final ImageCallback callback;

  ...
}

我想出了一个解决方案,我定义了一个工厂,

public class ImageDownloader {
  ...
  public static class Factory {
    private final HttpClient httpClient;
    private final ExecutorService executorService;

    @Inject
    public Factory(HttpClient httpClient, ExecutorService executorService) {
      this.httpclient = httpClient;
      this.executorService = executorService;
    }

    public ImageDownloader create(URL imageUrl, ImageCallback callback) {
      return new ImageDownloader(httpClient, executorService, iamgeUrl, callback);
    }
  }
  ...
}

现在,我不再注入ImageDownloader客户端的构造函数,而是简单地注入ImageDownloader.Factory并调用它的create()方法。

如您所见,这非常冗长且冗长。它也有一堆重复和样板。用 注释字段本身存在一些障碍@Inject,所以现在让我们忽略这种可能性。

Square 人提出了一个有趣的解决方案,使用提供程序。定义一个Factory接口,

public class ImageDownloader {
  ...
  public interface Factory {
    ImageDownloader create(URL imageUrl, ImageCallback callback);
  }
}

然后在一个模块中提供它,

public class ImageModule {
  ...
  @Provides 
  public ImageModule.Factory provideImageModuleFactory(
      final Provider<HttpClient> httpClientProvider, 
      final Provider<ExecutorService> executorServiceProvider) {
    return new ImageDownloader.Factory() {
      public ImageDownloader create(URL imageUrl, ImageCallback callback) {
        return new ImageDownloader(httpClientProvider.get(), executorServiceProvider.get(),
            imageUrl, callback);
      }
  }
  ...
}

(再次,来自 dagger-discuss@)。

MyImageDownloader是一个由另一个类注入的类注入的类,该类由另一个类注入,...,在@Module. 这一切都以某种方式*工作,并且所有类都在构建时找到。现在,要添加一个模块,我必须明确地让对象图知道它。

我一定遗漏了一些东西——注入一个新类很容易,但添加一个新模块却很乏味。

我的问题是:在实践中如何进行辅助注射?有人有例子吗?我应该如何使用ImageModule,如果有的话?

* - “不知何故”确实暗示它对我来说是部分魔法。

4

3 回答 3

18

因此,Google 的一些 Dagger/Guice 人员在一个项目中创建了一个名为 AutoFactory ( http://github.com/google/auto ) 的东西,其中包括 AutoFactory(代码生成的辅助注入)、AutoValue(代码生成的自定义值)类型)和 AutoService(自动生成 java 服务元数据文件)。

AutoFactory 几乎按照您的预期运行 - 它会生成您本来可以手动创建的工厂。这是一个非常早期的版本,我们计划了更多的灵活性,但它将生成一个工厂类,该类将采用包含一些 JSR-330 可注入依赖项和一些调用堆栈参数的类型,并将它们合并在一起以创建实例带注释的类型。

本质上,如果您正确注释工厂创建的类型,它将自动生成您编写的工厂。

例如,如果你创建你的类:

@AutoFactory
public class ImageDownloader {
  // Get these dependencies from the injector.
  private final HttpClient httpClient;
  private final ExecutorService executorService;

  // Get these from the caller.
  private final URL imageUrl;
  private final ImageCallback callback;

  ImageDownloader(
      @Provided HttpClient httpClient,
      @Provided ExecutorService executorService,
      ImageCallback callback,
      URL imageUrl) {
    // assignments
  }
}

AutoFactory 将生成:

@Generated("com.google.auto.factory.processor.AutoFactoryProcessor")
public final class ImageDownloaderFactory {
  private final Provider<ExampleClasses.HttpClient> httpClientProvider;
  private final Provider<java.util.concurrent.ExecutorService> executorServiceProvider;

  @Inject
  public ImageDownloaderFactory(
      Provider<ExampleClasses.HttpClient> httpClientProvider,
      Provider<java.util.concurrent.ExecutorService> executorServiceProvider) {
    this.httpClientProvider = httpClientProvider;
    this.executorServiceProvider = executorServiceProvider;
  }

  public ImageDownloader create(ImageCallback callback, URL imageUrl) {
    return new ImageDownloader(
        httpClientProvider.get(), 
        executorServiceProvider.get(), 
        callback, 
        imageUrl);
  }
}

(注意,我们需要对输出源进行大量清理,但上面的内容基本上是生成的,尽管格式不太好。)

然后,生成的类正确地是一个符合 JSR-330 的可注入类,您可以将其注入您的依赖关系图中(在 Dagger 或 Guice 中),它将为您创建这些对象,将调用堆栈状态与提供的依赖项混合在一起适当地。

您可以注入上述 Just-In-Time ,也可以@Provides在闲暇时通过方法提供。

你甚至可以让工厂实现一个工厂接口,然后简单地将两者绑定到一个 dagger 模块中,如下所示:

@AutoFactory(implementing = MyFactoryInterface.class)
public class ImageDownloader {
  // ... otherwise as above...
}

@Module(...)
class MyModule {
  @Provides MyFactoryInterface factoryImpl(ImageDownloaderFactory impl) {
    return impl;
  }
}
于 2014-04-02T03:38:39.793 回答
6

正如@xsveda 所说,对于辅助注射,您可能想要使用AssistedInject我在这篇博文中写过它,但我将在此处添加一个完整的示例以使事情变得更容易。

您需要的第一件事是依赖项:

compileOnly 'com.squareup.inject:assisted-inject-annotations-dagger2:0.5.0'
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.5.0'

那么您的示例如下所示:

class ImageDownloader @AssistedInject constructor(
  private val httpClient: HttpClient,
  private val executorService: ExecutorService,
  @Assisted private val imageUrl: URL,
  @Assisted private val callback: ImageCallback
) {

  @AssistedInject.Factory
  interface Factory {
    fun create(imageUrl: URL, callback: ImageCallback): ImageDownloader
  }
}

首先,我们不是用 注释构造函数@Inject,而是用 注释它@AssistedInject。然后我们注释必须通过工厂的参数,这与 AutoFactory 所期望的相反。最后,我们需要一个带有注解的内部工厂接口,@AssistedInject.Factory它有一个接收辅助参数并返回我们感兴趣的实例的方法。

不幸的是,我们在这里还有一个额外的步骤:

@AssistedModule
@Module(includes = [AssistedInject_AssistedInjectModule::class])
interface AssistedInjectModule

我们不一定需要专门的模块,即使这是一个有效的选项。但是我们也可以将这些注释放在已经安装在组件中的另一个模块中。这里的好处是我们只需要执行一次,之后任何工厂都会自动成为图表的一部分。

有了它,你基本上可以像往常一样注入工厂并请求你的对象。

于 2019-04-09T16:36:03.013 回答
0

您可以使用square/AssistedInject使用 Dagger 进行辅助注射

请在此处查看我的原始答案:https ://stackoverflow.com/a/53719342/2862474

于 2018-12-11T07:35:07.680 回答