From dagger-discuss@:
I have a class that gets some dependencies from the object graph, and other dependencies from a caller at runtime.
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;
...
}
I came up with a solution, where I define a Factory,
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);
}
}
...
}
Now, instead of injecting ImageDownloader
in the client's constructor, I simply inject ImageDownloader.Factory
and call its create()
method.
As you can see, that's quite verbose and long. It also has a bunch of duplication and boilerplate. There're some obstacles to annotating the fields themselves with @Inject
, so let's ignore this possibility for now.
The Square people have come up with an interesting solution, using providers. Define a Factory
interface,
public class ImageDownloader {
...
public interface Factory {
ImageDownloader create(URL imageUrl, ImageCallback callback);
}
}
and then provide it in a module,
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);
}
}
...
}
(again, from dagger-discuss@).
My ImageDownloader
is a class that's injected by a class which is injected by another class which is injected by yet another class, ..., which is referenced in a @Module
. This all somehow* works, and all classes are found in build time. Now, to add a module, I have to explicitly let the object graph know about it.
I must be missing something - it's very easy to inject a new class, but very tedious to add a new module.
My question is: how is assisted injection done in practice? anyone has an example? how should I use ImageModule
, if at all?
* - "somehow" does indeed imply it's partly magic to me.
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;
}
}