Looking for an example for Dagger assisted injection

帅比萌擦擦* 提交于 2019-11-27 21:17:24

So, some of the Dagger/Guice folks at Google created a thing called AutoFactory (http://github.com/google/auto) in a project that includes AutoFactory (code-generated assisted injection), AutoValue (code-generated custom value types) and AutoService (auto-generation of java services metadata files).

AutoFactory pretty much operates like you would expect - it generates the factory you would otherwise have hand-rolled. It's a very early version, and we have a lot more flexibility planned, but it will generate a factory class that will take a type that includes some JSR-330 injectable dependencies and some call-stack parameters, and merge them together in creating instances of the annotated type.

In essence it will generate the factory you wrote, automatically if you properly annotate your factory-created type.

For instance, if you create your class:

@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 will generate:

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

(Note, we have a bunch of clean-up to do on the output source, but the above is basically what is generated, though not quite as nicely formatted.)

The resulting class is then, properly a JSR-330 compliant injectable class, which you can inject in your dependency graph (in Dagger or Guice) and it will create these objects for you, co-mingling the call-stack state with the provided dependencies appropriately.

You can inject the above Just-In-Time, or you can provide it via an @Provides method at your leisure.

You can even have the factory implement a factory interface, and then simply bind the two together in a dagger module like so:

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

@Module(...)
class MyModule {
  @Provides MyFactoryInterface factoryImpl(ImageDownloaderFactory impl) {
    return impl;
  }
}

As @xsveda said, for assisted injection you probably want to use AssistedInject. I wrote about it in this blogpost, but I'll add a full example here to make things easier.

First thing you need are the dependencies:

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

Then here's how your example would look like:

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

First thing is that instead of annotating the constructor with @Inject, we annotate it with @AssistedInject. Then we annotate the parameters that will have to go through the factory, which is the opposite of what AutoFactory expects. Finally, we need an inner factory interface annotated with @AssistedInject.Factory that has a single method that receives the assisted parameters and returns the instance we're interested in.

Unfortunately, we still have an extra step here:

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

We don't necessarily need a dedicated module for it, even though that's a valid option. But we can also have those annotations in another module that is already installed in the component. The nice thing here is that we only need to do it once, and after that any factory will automatically become part of the graph.

With that, you can basically inject the factory and ask for your object as you'd normally do.

You can do assisted injection with Dagger using square/AssistedInject

Please check also my original answer here: https://stackoverflow.com/a/53719342/2862474

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!