This question is about a correct usage of Guice #Assisted and #Provides and also, how to do it.
The current design I refer to is something like this:
The class in the top of the hierarchy is also the only class that is exposed to the client (basically, the public API), its looks something like that:
public class Manager{
public Manager(int managerId, ShiftFactory sf, WorkerFactory wf);
// methods ...
}
As you probably understand, id is provided by the user on creation time (#Assisted?)
but the others are not, they are just factories.
The class Manager creates instances of the class Shift.
The class Shift creates instances of the class Worker.
Now, in order to create the class Shift we use its constructor:
public Shift(int managerId, int shiftId, WorkerFactory wf);
shiftId provided by the Manager and the rest are the same objects from Manager's constructor.
In order to create Worker we use 2 static factory methods (but it can be changed..):
public Worker createWorkerTypeA(int shiftId, int workerId)
public Worker createWorkerTypeB(int shiftId, int workerId)
workerId provided by the Shift class. and the rest is delegated from Shift constructor.
What is the correct, Guice-y way to do it?
Where should I put #Assisted? #Provides?
I really would like a code example of that, including the abstract module, because the code exmaples i've seen so far are not understandable to me just yet.
Thanks
At a high level, what you want is for your factories to hide the predictable dependencies so you only have to specify the ones that change. Someone who has an instance of the Factory should only have to pass in data, not factories or dependencies. I picture the interface like this.
interface ManagerFactory {
Manager createManager(int managerId);
}
interface ShiftFactory {
Shift createShift(int managerId, int shiftId);
}
interface WorkerFactory { // The two methods here might be difficult to automate.
Worker createWorkerA(int managerId, int shiftId, int workerId);
Worker createWorkerB(int managerId, int shiftId, int workerId);
}
class Manager {
#Inject ShiftFactory shiftFactory; // set by Guice, possibly in constructor
private final int managerId; // set in constructor
Shift createShift(int shiftId) {
shiftFactory.createWorkerA(this.managerId, shiftId); // or B?
}
}
class Shift {
#Inject WorkerFactory workerFactory; // set by Guice, possibly in constructor
private final int managerId; // set in constructor
private final int shiftId; // set in constructor
Worker createWorker(int workerId) {
shiftFactory.createShift(this.managerId, this.shiftId, workerId);
}
}
Note here that Manager doesn't care at all about workers—it doesn't create them, so unlike in your question, you don't have to accept a WorkerFactory just to pass it along to your Shift. That's part of the appeal of dependency injection; you don't have to concern a middle-manager (middle-Manager?) with its dependencies' dependencies.
Note also that none of the Factory interfaces or implementations are even slightly visible to your public API outside of constructors. Those are implementation details, and you can follow along the object hierarchy without ever calling one from outside.
Now, what would a ManagerFactory implementation look like? Maybe like this:
class ManualManagerFactory {
// ShiftFactory is stateless, so you don't have to inject a Provider,
// but if it were stateful like a Database or Cache this would matter more.
#Inject Provider<ShiftFactory> shiftFactoryProvider;
#Override public Manager createManager(int managerId) {
return new Manager(managerId, shiftFactoryProvider.get());
}
}
...but that's largely boilerplate, and possibly much more so when there are a lot of injected or non-injected parameters. Guice can do it for you, instead, as long as you still provide your ManagerFactory interface and you annotate a constructor:
class Manager {
private final ShiftFactory shiftFactory; // set in constructor
private final int managerId; // set in constructor
#Inject Manager(ShiftFactory shiftFactory, #Assisted int managerId) {
this.shiftFactory = shiftFactory;
this.managerId = managerId;
}
// ...
}
// and in your AbstractModule's configure method:
new FactoryModuleBuilder().build(ManagerFactory.class);
That's it. Guice creates its own reflection-based ManagerFactory implementation by reading the return type of the Manager method, matching that to the #Inject and #Assisted annotations and the interface method parameters, and figuring it out from there. You don't even need to call the implement method on FactoryModuleBuilder unless Manager were an interface; then you'd have to tell Guice which concrete type to create.
For kicks and grins, let's see the same thing with Google's code-generating AutoFactory package:
#AutoFactory(
className = "AutoManagerFactory", implementing = {ManagerFactory.class})
class Manager {
private final ShiftFactory shiftFactory; // set in constructor
private final int managerId; // set in constructor
#Inject Manager(#Provided ShiftFactory shiftFactory, int managerId) {
this.shiftFactory = shiftFactory;
this.managerId = managerId;
}
// ...
}
Almost identical, right? This will generate a Java class (with source code you can read!) that inspects the Manager class and its constructors, reads the #Provided annotations (n.b. #Provided is the opposite of FactoryModuleBuilder's #Assisted), and delegates to the constructor with its combination of parameters and injected fields. Two other advantages to Auto, which works with Guice as well as Dagger and other JSR-330 Dependency Injection frameworks:
This is normal Java code free of the reflection in Guice and its FactoryModuleBuilder; reflection performance is poor on Android, so this can be a nice performance gain there.
With code generation, you don't even need to create a ManagerFactory interface--without any parameters to #AutoFactory you would wind up with a final class ManagerFactory { ... } that has exactly the behavior Guice would wire up through FactoryModuleBuilder. Of course, you can customize the name and interfaces yourself, which might also help your developers as generated code sometimes doesn't appear well to tools and IDEs.
UPDATE to answer comments:
Regarding createWorker: Yes, sorry, copypaste error.
Regarding automation: It's because neither Assisted Inject nor AutoFactory has a great way to delegate to static methods, or to work with constructors that have identical assisted (user-provided) arguments. This is a case where you might have to write a Factory of your own.
Regarding Manager not needing a WorkerFactory: The only reason Manager would require WorkerFactory is if it's creating either a ShiftFactory or a Shift itself by calling the constructor. Note that my example does neither of those: You're letting the dependency injection framework (Guice) provide the dependencies, which means that the WorkerFactory is hiding in the ShiftFactory that Guice is already providing.
Related
Im interested what is the proper manner to construct object with dependencies which themselves have #Assisted params. An example will better illustrate:
Ordinarilly with dependencies that do not have #Assisted params you can simply have a complex heirarchy of objects which all have their dependencies and these get injected no problem, I can just get an instance of an object and all its dependencies will be generated and injected without me having to do anything.
But if I wanted to change it so some of the dependencies in the object hierarchy have #Assisted params then I have to create those instances myself using a factory ie:
public SomeConcreteService(#Assisted String string) {
this.string = string;
}
MyFactory myFactory = injector.getInstance(MyFactory .class);
SomeService myService = factory.getMyService("some string");
This would cause problems for the clean instantiation of the objects because I would have to manually create those instances. and pass them into the required object, which essentially renders the DI for that object redundant I think???? ie I would then need to use the new keyword and pass in all dependencies manually...
new MyComplexObject(myService, myOtherService)
How do I make it so that I don't have to manually build the object graph if one or more of the dependencies uses #Assisted parameters?
The question you need to ask yourself is, "why am I making this #Assisted, and who is going to create these objects?"
If you use the key to get an instance you need, then what you have is fine:
public class YourInjectableClass {
#Inject private MyFactory factory;
public void doStuff(String key) {
// You have a key, and your factory, so you can create the instance yourself.
SomeService service = factory.getMyService(key);
// [...]
}
}
But if you use the key to get an instance create an instance to create an instance to get what you need, then that seems problematic. That might be a better problem for child injectors:
public class YourInjectableClass {
#Inject private Injector injector;
public void doStuff(String key) {
// You need an OuterObject. So rather than calling
// new OuterObject(new InnerObject(factory.getMyService(key))), do:
OuterObject outerObject =
injector.createChildInjector(new OuterObjectModule(key))
.getInstance(OuterObject.class);
// [...]
}
}
Because your value is needed throughout the dependency tree, you can treat it as an injected dependency. This can be a little more confusing, but saves you from letting your dependencies care about instantiation details all the way down the line.
Alternatively, you can create a manual OuterObjectFactory facade which does manually call new. This may be a better solution for legacy code, but can help to follow the Single Responsibility Principle by ensuring that one class is responsible for abstracting away instantiation details.
N.B. I'm assuming that SomeConcreteService takes other dependencies that the object graph can provide. If not, then there's no reason to use injection at all: Give SomeConcreteService a public constructor and call new SomeConcreteService("your value here") where needed. Though Guice takes some pains to abstract away the use of new, it is also unnecessary to create data objects or dependency-light objects like HashMap or Date.
I have a situation where when I initialize some of my classes, some of the fields I need to be injected (e.g. references to factories etc) whereas some others are dynamic and created at runtime (e.g. usernames etc). How do I construct such objects using the GUICE framework?
Simply annotating the fields I need injected as #Inject doesn't work as they seem to not be set up when creating an object using the constructor. For instance:
class C {
#Inject
private FactoryClass toBeInjected;
private ConfigurationField passedIn;
public C(ConfigurationField passedIn) {
this.passedIn = passedIn;
}
}
If my understanding is correct (and I could be wrong), the fact that I'm creating a new instance of C via new and not through Guice means that no injection will take place. I do need to pass these parameters in the constructor, but also want some fields injected -- so how do I solve this problem?
A feature specifically matching "mixing injection and parameters passed" would be Assisted Injection.
class C {
// Guice will automatically create an implementation of this interface.
// This can be defined anywhere, but I like putting it in the class itself.
interface Factory {
C create(ConfigurationField passedIn);
}
#Inject
private FactoryClass toBeInjected;
private ConfigurationField passedIn;
private SomeOtherDepIfYoudLike otherDep;
#Inject public C(#Assisted ConfigurationField passedIn,
SomeOtherDepIfYoudLike otherDep) {
this.passedIn = passedIn;
this.otherDep = otherDep;
}
}
Now in your module:
#Override public void configure() {
install(new FactoryModuleBuilder().build(C.Factory.class));
}
Now when someone wants to create a C, they can avoid calling the constructor directly; instead, they inject a C.Factory into which they pass a ConfigurationField instance of their choice and receive a fully-constructed, fully-injected C instance. (Like with most well-designed DI objects, they can call the constructor directly.)
Note that this design is especially useful in a few ways:
You can use constructor injection, treat all your fields as final, and treat the object as immutable.
If you stick with constructor injection entirely, your object will never be in a partially-initialized state, and your API stays simple (call the constructor and your object is ready).
For testing, you can write any implementation of C.Factory and have it return any instance you want. This can include test doubles of C or its factory: Fakes, mocks, or spies that you create manually or by using Mockito, EasyMock, JMock, or any other mocking framework.
What you are looking for is "On Demand" Injections:
public static void main(String[] args)
{
Injector injector = Guice.createInjector(...);
CreditCardProcessor creditCardProcessor = new PayPalCreditCardProcessor();
injector.injectMembers(creditCardProcessor);
}
or for static things
#Override public void configure() {
requestStaticInjection(ProcessorFactory.class);
...
}
All explained very well https://github.com/google/guice/wiki/Injections#on-demand-injection.
Note:
Both of these things are code smells and should only really be used
for migrating old code over to Guice. New code should not use these
approaches.
With Google Guice or Gin I can specify parameter with are not controlled by the dependency injection framework:
class SomeEditor {
#Inject
public SomeEditor(SomeClassA a, #Assisted("stage") SomeClassB b) {
}
}
The assisted parameter stage is specified at the time an instance of SomeEditor is created.
The instance of SomeClassA is taken from the object graph and the instance of SomeClassB is taken from the caller at runtime.
Is there a similar way of doing this in Dagger?
UPDATE: As of Dagger 2.31 from January 2021, Dagger now natively supports assisted injection, which is recommended over the Square and Auto options. (Those other options still work, but may require extra setup compared to the native option.)
class SomeEditor {
#AssistedInject public SomeEditor(
SomeClassA a, #Assisted SomeClassB b) {
// ...
}
}
#AssistedFactory interface SomeEditorFactory {
SomeEditor create(SomeClassB b);
}
(original answer)
Because factories are a separate type of boilerplate to optimize away (see mailing list discussion here), Dagger leaves it to a sister project, AutoFactory. This provides the "assisted injection" functionality Guice offers via FactoryModuleBuilder, but with some extra benefits:
You can keep using AutoFactory with Guice or Dagger or any other JSR-330 dependency injection framework, so you can keep using AutoFactory even if you switch between them.
Because AutoFactory generates code, you don't need to write an interface to represent the constructor: AutoFactory will write a brand new type for you to compile against. (You can also specify an interface to implement, if you'd prefer, or if you're migrating from Guice.)
Because all the type inspection happens at compile-time, it produces plain old Java, which doesn't have any slowness due to reflection and which works well with debuggers and optimizers. This makes the Auto library particularly useful for Android development.
Example, pulled from AutoFactory's README, which will produce a SomeClassFactory with providedDepA in an #Inject-annotated constructor and depB in a create method:
#AutoFactory
final class SomeClass {
private final String providedDepA;
private final String depB;
SomeClass(#Provided #AQualifier String providedDepA, String depB) {
this.providedDepA = providedDepA;
this.depB = depB;
}
// …
}
Just like #xsveda, I also wrote an answer about this in this other question, which I'll also reproduce here.
Today, for assisted injection with Dagger 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.4.0'
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0'
Then here's how it can 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.
Yes, please check this Square project: square/AssistedInject
Currently it is not in 1.0 yet for purpose. They wait until Dagger will introduce a public API for registering those generated Module classes automatically - see this issue. With that you won't have to reference them in your Dagger code as in this example from README:
#AssistedModule
#Module(includes = AssistedInject_PresenterModule.class)
abstract class PresenterModule {}
Lets say I have a service called Guice service and here is its constructor
public GuiceService(IPayment payment) {
this.payment = payment;
}
And my code used to create it using an Enum
IPayment payment = new PaymentFactory.create(PaymentType.Cash);
NaiveService naiveService = new NaiveService(payment);
And I had to have a factory implementation somewhere. Something like this
public IPayment create(PaymentType paymentType) {
IPayment cardPayment = null;
switch (paymentType) {
case Cash:
cardPayment = new CashPayment(100);
break;
case Card:
cardPayment = new CardPayment(10, 100);
break;
}
return cardPayment;
Now I want to use Guice and I guess I want to use FactoryModuleBuilder.
What is the way to do it if I have more that one implentation of IPayment.
(e.g. CardPayment, CashPayment)
This works for one
install(new FactoryModuleBuilder()
.implement(IPayment.class, CashPayment.class)
.build(IPaymentFactory.class));
How do I implement the constructor ?
will it still get IPayment? or will it get the factoryImpl created by Guice?
Thanks
Your existing implementation is the best you can get.
Let's write out a general IPaymentFactory for clarity:
public interface IPaymentFactory {
IPayment create(/* ... */);
}
So instances of IPaymentFactory define one method, that takes in some number of parameters and returns an instance of IPayment. You could write an implementation yourself, and evidently you have, but Guice's FactoryModuleBuilder provides interface implementations like this one automatically. You never need to define anything else about that class: Guice will wire up the constructor for you, and bind it to IPaymentFactory so you can inject IPaymentFactory instances, call create(...) with your parameters, and get IPayment instances.
It looks like what you're going for is a factory that takes an Enum:
public interface IPaymentFactory {
IPayment create(PaymentType paymentType);
}
...but given that CashPayment takes one arbitrary parameter, and CardPayment takes two arbitrary parameters, and given that the selection between them requires a mapping to an arbitrary PaymentType enum, you haven't given Guice nearly enough information to construct the right object.
Guice FactoryModuleBuilder is designed more for combining constructor parameters with dependencies:
// Constructor:
#Inject public BitcoinPayment(
#Assisted long value, // varies by instance as a constructor parameter
BitcoinService bitcoinService // passed-in dependency satisfied by Guice
) { /* ... */ }
// Factory interface:
public IBitcoinPaymentFactory {
BitcoinPayment create(long value); // users don't need to know about dependencies!
}
// Factory binding...
install(new FactoryModuleBuilder().build(IBitcoinPaymentFactory.class));
// ...which lets Guice write the equivalent of:
public GeneratedBitcoinPaymentFactory implements IBitcoinPaymentFactory {
#Inject Provider<BitcoinService> bitcoinServiceProvider;
#Override public BitcoinPayment create(long value) {
return new BitcoinPayment(value, bitcoinServiceProvider.get());
}
}
On one hand, the factory is dumber than you think: It just combines parameters with dependencies to get one whole list. On the other, it's handy: you specify the dependency list once, and Guice does the rest.
In summary: FactoryModuleBuilder won't solve your problem, but it COULD help you create factories for CashPayment and CardPayment, which you could then inject into your manual PaymentFactory implementation (which will still need to exist in some form or another).
P.S. In your example, which might be a "toy problem" for demonstration, you may not need to use Guice. Guice is a great solution for service objects that require dependencies, but data objects (like a payment) or other objects that don't seem to need dependencies (like GuiceService or NaiveService) can be constructed directly using constructors. Once they start needing Guice-injected dependencies, it should be pretty easy to make them Guice-aware.
Suppose that I have two classes, first a class without any properties, fields or annotations:
public class B {}
And a class which gets B injected, like this:
public class A {
#Inject
private B b;
public B getB() {
return b;
}
}
Now class A is pretty useless until we use it, so there are two options:
#Inject it
Construct it manually, using the trusty "new A()"
If A gets injected, CDI manages it and is kind enough to inject B which has the implicit scope of #Dependent. Cool, just what I want.
However, if I manually construct A (let's say in a factory or a builder), CDI completely ignores my object and won't inject an object of type B.
Example I'm talking about when it doesn't work, here object a will always remain null:
public class Builder {
#Inject
private A a;
public static Builder ofTypeSomething() {
// do some magic here
return new Builder();
}
private Builder() {
// and some more here
}
}
Why doesn't this work?
Class A is a valid managed bean and has a valid scope, just like class B. Even if I add #Producer to the static method, it won't change anything (which is fine, cause the idea of the static method is to call it, not to inject Builder anywhere).
Dependency injection, while useful, is not magical. The way DI works is that when you ask the container for an instance of an object the container first constructs it (via new()) and then sets the dependencies (how this happens depends on your framework).
If you construct the entity yourself then the container has no idea you've constructed the entity and can't set the dependencies of the entity.
If you want to use a factory then most frameworks have some way of configuring the entity so that the container knows to make a static factory method call and not call the constructor of the entity. However, you still have to obtain your entity from the container.
Edit: This site seems to demonstrate how to use a factory in CDI.