square / dagger

A fast dependency injector for Android and Java.

Home Page:https://square.github.io/dagger/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Proposal: Dagger 2.0

gk5885 opened this issue · comments

For a while now, we have floated ideas about full-graph generation and how we might evolve Dagger from half static, half runtime to a fully static, fully generated, fully awesome DI solution. In working with Dagger in a large application on the server side we ran into some particular pain points that shaped how we might want a static solution to look. So, I present a proposal for Dagger 2.0 that hopefully bridges the gaps between what's suitable on Android and what makes sense for very large applications, with ridiculous numbers of bindings.

https://docs.google.com/document/d/1fwg-NsMKYtYxeEWe82rISIHjNrtdqonfiHgp8-PQ7m8/edit?usp=sharing

Everybody should be able to comment. Feedback on this bug and in the doc are both welcome.

I have also developed a prototype for this solution. It works pretty well, but the code is still slightly embarrassing. I will try to get that cleaned up and into a github repository shortly. I'll update the bug when that happens.

It's a lot to take in but exciting indeed! I'll have to pass through the
doc more carefully soon.


Jake Wharton
http://about.me/jakewharton

On Tue, Dec 10, 2013 at 1:54 PM, Christian Edward Gruber <
notifications@github.com> wrote:

I'm excited.


Reply to this email directly or view it on GitHubhttps://github.com//issues/366#issuecomment-30271953
.

Interesting design WIP. Thanks for putting it together!

OK, here's the (still somewhat messy) code: https://github.com/gk5885/dagger/tree/pipe-dream

I recommend taking a look at how this all comes together in the sample. I'm working thorough the features that enable the more interesting bits of graph composition. I'll update this issue and the sample code when I get that stuff running.

Just as a process FYI, the first few commits have gone in internally (by @gk5885) in Google's internal repo, just starting to lay out the APIs. Very quickly I'm going to branch off the 1.x stuff (per our discussions with Jesse and Bob), so we can start moving forward on 2.x as the main trunk of development, making the 1.x branch effectively a maintenance branch (though still aggressively maintained until we decide otherwise as a project.) So expect to see stuff before the end of the quarter.

sounds great.

With the ObjectGraph being deprecated will there be something equivalent to ObjectGraph#get(type)?

You'll have to use a @Component interface for creating types.

@Component(module = FooModule.class)
interface FooComponent {
  Foo getFoo();
}

Dagger will generate an implementation of this interface to which you can pass a module.

FooComponent fooComponent = new DaggerComponent_FooComponent(new FooModule());
Foo foo = fooComponent.getFoo();

While it seems like a lot to write for just one type, in practice you won't use it for a single type so the overhead of the code in relation to what you use it for won't be as drastic.

So what if you just have a Class<?> that you need to get an instance of?

For instance for dagger/jersey integration: https://github.com/johnlcox/dagger-servlet/blob/master/dagger-jersey/src/main/java/com/leacox/dagger/jersey/DaggerComponentProviderFactory.java#L180

You'll make an interface, like:

@Component(...)
interface MyApplicationGraph {
  public Thing1 getParticularThing();
  public void injectFoo(Foo foo);
  //...
}

And that is your graph. Dagger 2.0 generates the implementation, which you just use. We are considering a sort of compatibility thingy, where we turn ObjectGraph into an interface with the get(Class<?> clazz) method. In the generated implementations, the get() method (and inject() method) would simply have a conditional that routes those methods to the appropriate strongly typed getter/injector method on your interface, or throw an exception.

Generally, the new pattern would not be to bother with that, but it would allow for some existing infrastructure to be re-used while more robust infrastructure is built up.

Ultimately, the @Component interface YourGraph { ... } is more powerful, in that it permits you to offer complex types (with concrete type variables, like List<String>) which we couldn't offer through the generalized get() method. So this above-mentioned mapping would largely be intended for backward compatibility with existing infrastructure.

Actually, arbitrary last-minute instantiation via dagger was never viable - if you don't express the class Foo via @Module(injects=Foo.class) then you can't ask graph.get(Foo.class) for an instance anyway. In this case, you just make a method public Foo getFoo(). But for arbitrary classes, that was never how Dagger was designed.

Hmm. Looking at the Jersey example, it seems like it is actually building a lot of reflective API on top of Dagger... but I think the ObjectGraph-compatibility approach might suit there. That said, this is particularly intense reflective analysis on what is supposed to be a compile-time framework... I'm thinking some of it could be potentially replaced with generics - instead of all the reflective lookup just have DaggerInjectedComponentProvider and have your component be T, instead of ObjectGraph. But that's off the top of my head. I'm going to build (or collaborate on) some similar infrastructure around servlet engines and for android internally in the next six to eight weeks, and I think we'll learn a lot about how to best harness Dagger 2.0 in other infrastructure.

That definitely makes sense. The dagger-jersey project was a quick attempt at getting dagger integration with Jersey and it works, but does require marking a lot of classes as injects on the module.

This happened.