Ran into an issue where it would have been nice to have a mechanism like the AfterInject annotation that is in Android Annotations (see https://github.com/androidannotations/androidannotations/wiki/Enhance-custom-classes#executing-code-after-dependency-injection).
Basically I wanted to do some work when my object was created by Dagger using values that were injected.
To be more concrete say I have these classes:
class Foo
{
@Inject Baz baz;
@Inject Foo() { }
}
class Bar extends Foo
{
@Inject Bar() { }
}
I would like to invoke a method on baz inside the constructor of Bar, but baz has not been injected yet. I don't really want to require making an explicit call to trigger the action. Switching to constructor injection would make sure the value was available in the constructor, but is a pain in the butt as there are a bunch of classes that extend Foo and doing so would add a lot of boilierplate to all of them to pass up the baz.
With an annotation like AfterInject I could do something like this:
class Bar extends Foo
{
@Inject Bar() { }
@AfterInject void initialize() { baz.doSomething(); }
}
That annotation would tell Dagger to generate a call to that method after the injections are made.
The way I worked around it is to use method injection and override the method in the subclass, but that won't always work well.
NInject DI framework has a different approach and instead uses IInitializable or IStartable interfaces to declare a method to call after injection. Either approach would work, although an issue I can see with this approach is that in a hierarchy like this there could be an issue with superclasses also wanting to declare such a method.
Was going to suggest an alternative of just allowing @Inject on a parameterless method, but found out that Dagger already supports that:
class Bar extends Foo
{
@Inject Bar() { }
@Inject void initialize() { baz.doSomething(); }
}
Which would work in my case since the parent class field would be injected first. But to be more general, ideally what you would want is to have the generated injector call all parameterless inject methods last. I could not easily determine if that is the case, but please make it so if not.
JSR 330 states:
Constructors are injected first, followed by fields, and then methods. Fields and methods in superclasses are injected before those in subclasses. Ordering of injection among fields and among methods in the same class is not specified.
Sounds like that is enough of a guarantee for you?
It is enough for my example as I said, but it would be a good idea to make a distinction between setter injections (methods that take parameters) and injection methods that take no parameters. The setter methods should be called before the ones that take no parameters. That now is all this request is asking for.
Why do you need multiple @Inject methods? Can't you invoke them all the same? Or do something like this:
class Bar {
@Inject void injectMembers(Foo foo) {
useBaz();
}
// @Inject // no longer needed
void useBaz() { ... }
}
That assumes there is only one setter. But consider an example like this:
class Bar extends SuperBar
{
private Foo foo;
private Qux qux;
@Inject void setFoo(Foo foo)
{
if(!foo.buzzled()) throw new IllegalArgumentException("foo must be buzzled");
this.foo = foo;
}
@Inject void setBaz(Baz baz)
{
qux = baz.toQux();
}
@Inject
void start() { foo.doSomething(baz); }
}
As it stands now this example is not guaranteed to work because start could be called before the setter injections. I can see no case where it would make sense to call a no argument @Inject method before @Inject methods that take actual values.
Field injections are done before method injections but these setters cannot really be made into field injections without major consequences. The fact that they are setters and not fields may actually be dictated by the super class.
I realize that JSR-330 does not guarantee this ordering, but I think it only makes sense to have such an ordering.
Why doesn't this work?
class Bar extends SuperBar
{
private Foo foo;
private Qux qux;
@Inject void setStuff(Foo foo, Baz baz)
{
// pre start
if(!foo.buzzled()) throw new IllegalArgumentException("foo must be buzzled");
this.foo = foo;
qux = baz.toQux();
start();
}
void start() { foo.doSomething(baz); }
}
If the @Inject methods are specified across types, then the JSR330 order applies.
We're not in a position to make guarantees that aren't specified by JSR 330. I think collapsing your @Inject methods is a pretty good option though.
Most helpful comment
JSR 330 states:
Sounds like that is enough of a guarantee for you?