GREG KICK: Hi, I'm Greg Keck. I work at Google on the
Java core libraries team. We've brought you
such things as Guava, some contributions
to Guice, and full of other things, here
and there over the years. But most notably Guava. In the past, I worked
on Data Liberation, which people may or
may not have heard of. Takeout, in a former life I
wrote JavaScript for that. And now I am working on
dependency injection, and specifically not Guice, but
Dagger, and now the redesign Dagger 2. That's me, I want
to do a quick couple questions about you guys. When I say injection
raise your hand if you know what
I'm talking about. Good news, the first couple
slides will be real quick. And then, as we talk about
dependency injection, you know there's been many
frameworks over the years for getting that done. We have, for example Spring. Any spring users? All right, and some groans
for Spring, sorry Spring. How about Guice? OK, a few fewer. And then has anybody
had the opportunity to pick up Dagger 1? Very few. So it's good to keep in
mind, as we move along. Because of the
microphone situation, normally I would
say interrupt me, but I think we're doing some
recording, we've got some mics, there will be time
for Q&A at the end. All right so let's get started. If the presentation works. Hey, almost. There we go, perfect. So like I said, I just
want to do a quick overview of what dependency injection is. Everybody seems to have
a pretty good idea, but let's just make sure we're
all started on the same page. Dependency injection is a
nice little design pattern for your Java classes,
and Java user group-- we're going to talk about
Java whole way through. And on the left
over here, I have a simple fictitious CoffeeMaker. If we were going to model
a CoffeeMaker in Java. And we're going
to pretend like it has only a heater and a pump. And so in order to make
Coffee with my CoffeeMaker, I need a heater, in this
case an electric heater. I need a pump, in this
case of thermosiphon. And because a
thermosiphon requires heat, that's the thermo, we
give it the heater as well. And so my CoffeeMaker class has
a method called make Coffee, and if I were to write my
application from a main method, I would make a new CoffeeMaker
tell it to make Coffee. That's without
dependency injection. Everything that I need to
make Coffee in my CoffeeMaker, I instantiate myself
in the constructor. Dependency injection
takes the dependencies of that class, the things
that it needs to do its job, and has been passed in
rather than created yourself explicitly. And that's the highlight
portion over to the right. The constructor just
takes two parameter, sets them, and relies
on somebody else to give it its dependencies. Dependency injection, go figure. The main method down
there is the example of how that might be done. In order to make my CoffeeMaker,
I must first make my heater, must first make my pump, then
pass it to the CoffeeMaker to make Coffee. Great? Great. The reason we do this,
or at least the claims that I'm going to make for
the reasons that we do this, is you can do things like easily
swap in fakes for testing. If I'm going to write a unit
test for the CoffeeMaker, I could make a fake
heater rather than an actual electric heater,
because electricity is expensive. We can also easily reconfigure
these different types. If I, instead of
an electric heater, want to use some crazy
solar powered heater, then I can go ahead and do that. I could also kind of do
development a little easier in parallel. Somebody else can
work on the heater while I work on the CoffeeMaker. If they change the constructor
for the electric heater that doesn't affect my code,
which sounds great. And all that wiring that
happened in the main method, and I want to be explicitly
clear, we can do that wherever. People have come up with all
sorts of ways to get that done. The part that we
want to focus on, is passing the
parameters in rather than making them yourself. In pictures, this
looks like this. I have my CoffeeMaker, it
includes the thermosiphon, it includes the electric heater
with dependency injection. Take all those things apart,
and via the interfaces, I declare my dependencies. Everything's nice and separate. So what's the problem? Well for big applications,
for big non-coffee related applications, taking
all those dependencies, wiring them altogether, there
could be a ton of boilerplate. This is, ironically, part of
the implementation of Dagger 2. Because you can't you Dagger
2 in Dagger 2, go figure. So I have to do all
of this stuff by hand. This is just one tiny
little confusing snippet that I have to manage myself. It's a pain. The biggest portion of the pain
is that all of those things have to be in the correct order. Dependency request
factory has to come before provision binding
factory, because it requires a dependency request
factory, yeah blah blah blah. All the way down, it's
ugly, it's a nuisance, we don't want to have
to do that by hand. And some people say,
yeah that's true, but I can manage it myself. No really, I'm a great software
engineer, I've got this. As examples, we had
an Android application that had about 3,000 lines of
that, because they-- I looked, it was 3,000 lines. I checked before I
wrote the presentation. 3,000 lines of exactly this. For a large server-side
app, it actually flushes out to be about 100,000
lines of exactly this code. It's depressing,
it's monotonous, we're going to try our best to
not have to do that by hand. The question is, how
do we get the benefits without the boilerplate? Well, we started back in
2002 with Spring 1 and 2. This is why I
asked about Spring, we love Spring back at that. It was revolutionary. It popularized dependency
injection, or inversion of control, or the
Hollywood principle, or what you want to call it. You had a way to declare
all of your dependencies, and it figured out that really
monotonous ordering for you. No matter of how you shifted
your dependencies around, it would always instantiate
things in the right order. Everybody was really
happy with that. And the best part, was
but all of the objects that you were doing,
you are manipulating, with your dependency
injection framework stayed exactly the same. Well that sounds like magic, how
did it stay exactly the same? It was XML. I'm sorry. It looked like that. It was cutting edge at the
time, let's not make fun. You would define
a beans.xml file, and you'd have little snippets
of XML here and there. But the things to
note, the good things, the things that we still
do today; it's declarative. There's no logic here,
there's no for loops, there's no anything like that. You say that I need to make a
CoffeeMaker, it takes a heater. Refer to the heater
that I've defined below. That heater, it's
an electric heater. That's the class
we want to use it. Also takes a pump, you
know, so on and so forth. And the point to
note specifically, is that those things do not
have to appear in order. That's the problem
we're trying to solve. We don't want to have
to constantly reshuffle every type of thing when
we swap a dependency. We don't want to have
to manually figure out the ordering of a
directed acyclic graph, for those of you that
love graph theory. And it ends up
looking like this. It's the exact same DI
picture, but now we've applied some XML to it. Where we used to have
a CoffeeMaker class, we've got this
little snippet that represents the CoffeeMaker. We have those constructor
our references. That's what gives
you your dependencies between all of those things. And the bean factory
is the Spring class that sort of wraps that all
up and does the work for you. Great. So like I said, solve
the ordering problem. It also solves instance
management and scoping. Feel free, if you're
curious to talk, or if you want to ask
about scoping later, it's kind of a complicated
topic we can Q&A it later. But for the most part,
we'll just take it on faith But it does that. But there were issues. Possibly more issues
than solutions, as my completely
jaded bullet points might lead you to believe. XML is not so good. We don't like XML. And all of the validation
of that configuration file happened at runtime. If you made a typo
anywhere in one of the names of one of your
bean definitions, or bean references, you were going
to start up your server before you found out about it. Same thing with validating
the graph itself. Even if your configuration
file was great, at runtime we have to figure
out to make sure that all the dependencies
were satisfied. If you forgot to
constructor parameter, you can't make an object. It also had some issues with
tracing the application flow. Trying to swap back and
forth from Java to XML, and it was a mess. And a map-like API,
that we will talk about in just a little bit. So that was Spring. Guice came around
somewhere around 2006. I looked at the very first CL
for Guice, it was adorable. But all the way back
to the mid 2000s. And Java 1.5 had
just been released up and coming somewhere
around there. And Guice said, I can use
all of these new language features like
annotations and generics to start doing the things that
I used to have to do an XML, now I can do in Java
with the annotations that I have available. And what that did was it
decreased the configuration, and decentralized
the configuration. No more giant XML files,
and the annotations that defined your configuration
were close to the code. Great. And entirely written
and executed in Java. Quick informal poll,
how many people have installed the Spring
Eclipse plug-in that tells you how the XML relates to the Java? Yeah, there's like three people
because getting it installed was sort of a pain,
I don't blame you. So now we want to talk about,
where did all that XML go. How do you do this in Guice. Who can spot it? Who can spot the one little
bit of configuration? That looks remarkably
like the CoffeeMaker I started with, except that guy. It's barely any
configuration at all, but just applying one
annotation to that constructor says I now participate
in dependency injection. A little better
than angle brackets. We also have these things at
say how the objects relate to each other, how
abstract classes relate to concrete classes. And for that, we have a
little bit more work to do. The top is an example
of how you might do it with the Java API
using class literals, and the bottom defines how
you might do with a method. So in the top example, I want
to say that my heaters are all going to be electric heaters. In the bottom, I say
that I want pumps, and I'm going to use
thermosiphons for my pumps. And that's the,
again, 100% in Java, but expressing all
the same concepts that we saw in XML, but
now in just pure Java code. Back to our friendly graphic. We're kind of showing how
that all ends up working out, is the same CoffeeMaker
was now defined by that @inject constructor. So much easier than XML. We have that provides method
that defined the pump. We can take it on
faith that there were @inject constructors
for the thermosiphon. Guice actually has a nifty
little feature, where if you don't have an
@inject constructor, it will just assume
default constructors, work so the electric
heater gets to participate, and we have that binding. The exact same concepts,
just in a little bit different
configuration language. And rather than bean factory,
we wrap it all up and injector. The one last thing
I want to point out is that in the
Spring XML version, in order to find the heater, we
had to define two references. We had to say that the
CoffeeMaker depended on a heater, and
the thermosiphon depend on a heater. Now we have one
definition for the heater. We are making huge
strides towards collapsing our configuration language. If for example,
you had a database that you needed to access
from 10 different places in your application,
you get to bind it once instead of having a line
of XML for each of the 10 places you might need to use it. That said, you might
also want to note that there are two
ways that we bind abstract types to
concrete types. Or, in this case, interfaces
to concrete types. We don't generally prefer
two ways to do things, and that tended to get
a little confusing. But this all sounds
great, what's the problem. Why is juice not
our silver bullet? We still have a lot of
issues with runtime. Guice users, how
many people have seen stack traces
exactly like this? How many people found
them immediately helpful? For those of you in
video land it was zero. The problem is, it's the
same problem that Spring had. You have to start
up your application, or start up some integration
test, or something on the like, and you get this stack trace,
that all it's trying to express is you forgot a binding. You forgot, in this case,
a binding for the pump. What happens if you
declare data binding twice? An equally sort of convoluted
runtime stack trace that has a lot more noise
than we would probably prefer. And this has been long,
basically since day one, a source of frustration
for Guice users. Another question
might be, how do we debug a Guice application? We're creating
all these objects, what happens if we
turn on the debugger? The top example is what happened
when we did it manually. Read two stack frames. One was the main method, the
other one was the CoffeeMaker. In Guice land, the exact
same breakpoint becomes that. It's a little frustrating. There's a little more
to trace through. There's a big run time process
that you have to deal with. Or worse yet, if you
enable the wrong feature, you will just get breakpoints
that your debugger doesn't know how to deal with. You can't possibly
run a debugger against some of
these applications. And you get something
curious things in the middle of
those stack traces. CoffeeMaker$$EnhancerByGuice$$,
some hashCode, $$FastClassByGuice$$,
more garbage. That's not particularly
helpful in most cases. And if you try to go figure
out what that thing is, it's going to tell you source
not found over, and over, and over, and over again. Not the friendliest experience. But worst of all, and
this is the same problem that Spring had, the
code becomes untraceable. How do you know
which implementation of [INAUDIBLE] going to use? Now you have to go
track down a binding. Tracking down a
binding sort of feels like getting a big pile of
LEGOs, somebody saying, OK, there's a house, you figure
out how it was put together. It's a little
daunting sometimes. And if you tried
to do things like, well OK, Eclipse
has a feature where I say find all the
places that somebody's calling my constructor. In your Guice
centric application, there'll be exactly zero
places that somebody is calling it the constructor. Because it's all
done by reflection. So your normal
tools that you might use to traverse your code? Not so helpful anymore. And finally the configuration
we laid out in Java. It's dynamic, and it
can be conditional. In this case, this
is a snippet out one of those
configuration modules. And somebody did
something very earnest. They said OK, my application
under some conditions, I want a certain type
of authentication. Under other conditions,
I want a different type of authentication. So in the middle of my module, I
checked to see what a flag did, picked one of the other. That seems like a perfectly
reasonable thing to do. But back to the LEGO analogy. If you imagine that
big pile of LEGOs, and every time you try to
put that house together the instructions kept
changing, how easy might that be to figure out how
the pieces might fit. It can be frustrating,
to say the least. So given all that, we've
heard some good news and bad with Guice. We've got binding discovery. I could just find
those things that are annotated with @inject. They get to participate without
a lot of extra configuration. So we greatly reduce
the configuration. No duplication,
things like that. And that configuration all lives
right next to the Java code, we don't have to go to a
separate XML file to find it. We can just be right
there go take a look. And it's all in pure Java. All of these things sound great. And for that reason,
Guice has become-- I can't think of an
exception-- Guice is almost certainly the most popular
framework that we have in Google applications for Java. It's used virtually everywhere. I mean hundreds and
thousands of classes are being managed with Guice. But it had issues. The runtime validation of
the graph, still a problem. Untraceable application
flow- pile of LEGOs-- still a problem. Synthetic classes that you
can't inspect, and have to go look at the
Guice source code even guess at what
they might be doing. Still a problem. And that map like API. So, Dagger 1 came along. Dagger 1 actually is not--
so, whereas Guice was a Google effort, Dagger 1 was former
Googlers working at Square. They decided to take their
crack at the problem. And they basically said the
configuration API-- basically all the solutions in
that solution column-- all that stuff was great. So we just take all
the bits that juice API that you can
inspect statically, that was the caveat. Everything that the compiler
knows ahead of time, and you don't have
to execute in order to figure out the behavior? That's the part that we want. We want this thing
to be-- your compiler will know what's going
on, you don't actually have to startup a
server to figure it out. That's the API. And then for the
feature set, we're going to toss out everything
that may be a little dangerous. Guice was used to great effect,
but if people are really frequently shooting themselves
in the foot with a feature, let's toss it. And then, the combination
of those two things let them go through each of the
dependency injection features that you might want, and stop
doing the things at runtime, and start doing them at
compile time with annotation processors. And this is a bit of the magic. Is that error
reporting now happens at compile time in Dagger 1. We'll get to some
examples of that. But the other bit
is you can just do things more efficiently. If you don't have
to do reflection. No reflection is always
cheaper than reflection if you're doing the
exact same thing, so let's do no reflection. And we take all of
that overhead out of asking for a new instance. The graph looks almost identical
to what we saw in Guice. The difference is
that where we used to have that API where
you could bind things with class literals, now
everything's a provider method. It's a little more
verbose, possibly, but that's the part that
we could know statically. The compiler can look
at that and figure out how your application
is wired together, because it's got a return
type, and it's got parameters. The other difference which
we don't quite show here, is that whereas in
Guice, abstract module was a class you had to inherit
modules, now an annotation. Really not that
interesting a difference. But what happens when
we forget bindings? I claim that we got
errors at compile time, and we do in fact do. That little red squiggly is
Eclipse dutifully reporting that there is no
binding for pump, it was required by the
CoffeeMaker via the drip module. And javac will tell you,
Eclipse will tell you, whatever your IDE will tell you. That happens immediately
as you type in Eclipse, because of its
constant compilation. We no longer have to start
servers to figure out that we have missing bindings. The development life cycle
becomes very, very quick. What happens if you
declare a binding twice? The exact same thing. You get a red squiggly line
on each of the duplicates. It tells you exactly
what they are, and it tells you as immediately
as compile time can make it. What about debugging? Better. I will argue that it's better,
but it's not quite perfect yet. The thing you will notice,
is that we still have weird classes with a $$ sign in them,
but at least we tried to give them friendly names. We call them inject adapter. It adapts your class to
injection, go figure. So we give them names
meant for humans to read. But the most important
bit, is those inject adapters have source. You can look at it, all the
time, without exception. You can always go look at
exactly what it is doing. So you have a clear path of
code from your main method all the way down to the
CoffeeMaker constructer. So I claim that you could look
at the code, let's do that. Uh oh, that's a lot of not
so friendly looking code. It does generate code. You can trace through it. Maybe a lot of time you
probably don't want to. There's a lot of stuff
going on, there's strings that look
like class literals. There's actual class literals. There's asking
for class loaders. It's certainly a
faraway as from perfect. But we do all sort
of boil down to kind of the holy grail
of this is what we're trying to do
in the first place, we've just got a
lot of extra cruft that we might want
to get around. Sort of toss out. So, what did Dagger 1 do for us? Well, we get compile
time validation of big portions of the graph. We saw that you get
errors pretty quickly. Easy debugging, we can have an
entirely concrete call stack. And highly efficient provision. I think I probably should
make that a little clearer. Did you notice that in creating
my new CoffeeMaker instance there is no reflection? Reflection is not used to
instantiate a CoffeeMaker in any way, shape, or form. Reflection is used to figure out
how everything fits together. So if you have applications
with sensitive start up times, or you're creating
graphs per request, that's going to be
an issue for you. But if you already have
a graph, and you're just asking for instances, it's
going to be nice and cheap. The issues are
that-- I think it's pretty-- I feel a little
bad calling it ugly. I had a hand in some of it. But it's a little ugly. And it's also a runtime
graph composition. The reflective part where
you wire everything together? That still runtime. So there are still
classes of errors that you can see at
runtime, which is not ideal. And reflection makes inefficient
graph creation sometimes. And if you want to trace
through your application for creating an instance? Great, everything's good to go. If you want to trace through
your application for how to compose your graph, that's
where we're still a little lacking. And that map-like API that
we just can't seem to kick? Still there. So we got together one day. Said, alright, how do
we finally take a shot at fixing these problems
once and for all. Or better yet, what
are the problems? And I continue to argue that
dependency injection has worked since roughly 2002. And certainly as of Guice in
2006, when you do things right, everything's easy. The parts we need
to focus on are what happens when
things go wrong. And particularly traceability. I want to be able
to have a navigate through the entire application. All parts of it. Creating the graph,
getting a new instance. With eclipses find usage
or open declaration. Those nice little hot key
that we all know and love, they should never not work. And I also want to
trace through code that looks kind of like
what I wanted to write. If I were to undertake
doing 100,000 lines by hand, I want the code that's generated
to look exactly, within reason, exactly like what I would
have written myself. We also want to clarify
the API a little bit. Simpler module declarations. They can get kind of
convoluted sometimes, because of how things
are composed at runtime. And no more maps. That map-like API, we're getting
rid of it once and for all. And then performance. Performance was actually
one of the big motivators for why we want Dagger 2. The Android, youre
mobile device, had pretty good
success with Dagger 1. Because you start up your
application once for the most part, and then you ask
for new activities, you ask for new things. And that reflection cost
that they were paying, they were paying
pretty infrequently. If you wanted to use
Dagger on the server side-- and we have been using
Dagger on the server side, on one of our larger
search applications, one of our larger
search services. The per request overhead of
creating big object graphs was really starting to weigh
pretty heavily on Dagger 1. And we'll talk about
some of the results for having moved on to Dagger 2. The other thing
is, we don't want to code around the
perk framework. Every time you find
yourself caching something because it's slow,
that's the burden we're placing on our users
that we don't want to. And we make sure
that no one ever has to do that with Dagger 2. So what we do? How do we do it? More or less we like the
API for configuration. We liked it in Guice, we
continue to like it in Dagger. It works, stick with
the configuration API. Those are provides methods
@inject constructors. All those things that JSR 330
defined for us, we're happy. The compile time code. We want to generate nice, simple
code for the entire stack. No more reflection. We're getting rid of
reflection entirely. That was a bit of a tricky
goal, we'll see how we did it. We also want to validate all
framework and configuration preconditions at compile time. If we don't have
reflection, and we don't have a runtime
presence, we obviously need to check all the
errors at compile time, otherwise your application would
just fall over and not compile. And then we finally wanted
to unify user defined types with generated
types, but we wanted to do it in a way that
was limited, clear, sort of intuitive. Something that you don't have
to really have to figure out. And that's, a lot of times,
the challenge with user generated code. And for that, we drew
inspiration from a framework, project, whatever you want
to call it, called AutoValue. It you are sick of writing
equals hashCode and toString, I highly recommend you
go look into AutoValue. Different project, also
code generation, that's where inspiration came. Go take a look. But what AutoValue did, in
the sort of the montra they set forth, was step one
is constrain the problem. Make the problem solvable
using code generation, and then generate the only
reasonable implementation that anyone would ever expect. Seems straightforward. So that was our strategy. Here's how we did it. We started with JSR 330. That's the thing that defines
@inject, a couple scoping annotations, all those DI
things that you're probably familiar with having
used the DI frameworks. The funny thing is it
only actually defines one non-annotation type,
it is called provider. Provider is an
interface, and it just says give me an instance of
whatever I'm a provider of, which seems pretty
straightforward. In our CoffeeMaker case-- I'm
sorry, I knew it would happen. There's a typo. That should say "provider
of CoffeeMaker," not "Provider of T". But imagine that said
provider of CoffeeMaker. By the time anyone on
YouTube sees these slides, it will say provider
of CoffeeMaker. So for provider of
CoffeeMaker, you just say, hey, I want
an instance called get, you get a CoffeeMaker. That seems pretty
straightforward. The trick is figuring
out how we need to implement-- you
know, what we generate the implements provider that
will work for our DI framework. Here's what we make. For a CoffeeMaker provider, we
call it CoffeeMaker$$Factory. Quick note about
the dollar signs, is anything that we don't
expect users to explicitly have to deal with, we put
double dollar signs in it. If you find yourself
typing in your IDE, and you type two dollar signs,
you're doing the wrong thing. But because they have
to become public classes for a variety of reasons,
we couldn't just hide them, so people couldn't touch them. We just had to make them ugly. So CoffeeMaker$$Factory
basically does exactly what we want to do in that
original main method, but instead of asking
for new instances, it just asked for the
provider of a heater, and the provider of the pump. It knows exactly one
link in the graph. It says, in order to make a
CoffeeMaker, I need a heater, I need a pump, so get the
provider for those things, call get(), return
a new CoffeeMaker. And that's all happening
right down there. With the layer of
provider indirection, it's otherwise exactly the same. And we generate that for you. And I would argue
that most people can, if they're familiar
with providers, had not having seen
this for the first time, it looks pretty straightforward. It's certainly a lot shorter
than our previous version. The other thing is, we
all have provides methods, which is a method
that provides a value. We saw those in the
previous examples. The only difference between
a factory for a constructor, and a factory for
provides method, is that you just get an
instance of the module invoke the method. That's happening down there. In order to provide a pump,
I need a thermosiphon, because that's my
implementation of pump. I generate code the
just calls the method and passes in the thermosiphon. Pretty straightforward
there too, I think. Now we get to talk
about that map-like API. This is the client API. This is how you actually
ask for a CoffeeMaker. In Spring, it was pre-generic,
so it was make a bean factory, pass in the string, cast. In Guice, we had Java 1.5, so we
got to be a little more clever. We got to say create an
injector by passing it on my configuration,
and then get an instance of a CoffeeMaker,
and automatically does the right things generics. But we're still passing
in class literals, so obviously we're still
in reflection land. And Dagger 1, like I said,
that was exactly the same. It was still reflection. It still looked, more or less,
like a map of class to object. In Dagger 2, this
is the crux of it. This is the part of the changes. This is the part that we
are doing most differently. We say, here is a
CoffeeMaker maker component. And component is the
word for the interface that Dagger 2
needs to implement, that just gives you instances
of all the types you defined. So as a user, I say
I want a CoffeeMaker. I define an interface that
says give me a CoffeeMaker. Just a method, no arguments,
returns a CoffeeMaker, and I toss an @component
annotation on it. I list out all of
the modules that I need to make my component. And if any of them are missing,
we get the compile time errors that we expect. And what it does, is it
generates a class with the same name as the component, but
prefixed with Dagger_ that knows how to take all of
those bindings together, and create your CoffeeMaker. So, we just say
Dagger_CoffeeMaker.create();, and we didn't implementation
of that interface. We never have to hold onto
that concrete type again, but that thing knows how to
make CoffeeMakers for me. And I just called the
method with the method name that I gave it. Sounds like magic. Here's the magic. We literally write all of
that horrible boilerplate that we used to have
to write by hand, exactly as you would
have to write it if you wrote it by hand in
the generated implementation. It is all of the same logic,
in all the same order, just written out for you. I am going to admit to this
being slightly different than what you would actually
get out of the tool, cause I had to kind of
keep a little more compact to live on a slide. But with very few modifications,
this is exactly what you get. It just goes through, says
here are all the things that don't have dependencies,
I can make them first. And then all the things that
I need, wires them all up, stores the references
to the provider. And the CoffeeMaker method,
the getCoffeeMaker method that I defined I my interface? The implementation is,
invoke that provider. Which in turn, invokes
all the other providers. Which in turn, invokes
all of the other providers all the way down. And as far as we can tell
it is the single most runtime efficient version
of dependency injection that you can possibly have. We have yet to dream
up anything better. And there are a couple layers
of indirection in here, a couple extra provider objects,
and for the most part, hotspot seems to do its job
and make all of those things as cheap, with all the inlining
that we could hope for. So what have we done. We have compile time
validation of the entire graph, because we're generating
the entire graph. We have super easy debugging,
because you could literally just trace through,
line by line, and watch it invoke
all of these providers. Fully traceable, you can
see all the find usages. Let's go back to that slide. Every one of those
factories that we generated in the first step, we have
references right there. We can just F3 in
Eclipse, open declaration. And the API is finally,
once and for all, a Java object just like
everything else. I say I have this interface,
here the things that I need, that's what I work with instead
of map type things with class liberals. And most importantly,
performance. I guess this is the part
where I could tell you exactly how performant. So we had a pretty
large search service that's serving a lot of traffic. And the more Dagger
1 we adopted, people were very happy with
the compile time errors. But people, our SRE's, the
people in charge of monitoring just how efficiently
things are running, how many CPU things are taking. They started to come
knocking on my door, saying this is getting
worse, and worse, and worse, and we have to stall some of
the migrations to Dagger 1 because we're
burning too much CPU. So I kept saying, hold
on, we'll get Dagger 2. It'll solve all of
our problems, it is theoretically the best we
can do, so obviously that'll work in practice, right? It was earlier this
week that I finally got to wipe the sweat off my
brow, to the tune of about 13% of per quest CPU
at Google scale. That's a lot of CPU. We made every single request
all of a sudden 13% cheaper by moving just
dependency injection. That was a very big win. In Android world, that could be
13% of your application startup time, depending on how your
applications are wired. But I think most
importantly, I continue to argue that you just
can't do any better. But I'm not going to lie. There are some issues. There are some things
we traded in order to get all these great benefits. It's less flexible. Reflection is nice
and magic because you can do virtually
anything you want. You can inspect your
entire application, permute it crazy ways,
generate byte code. We don't let you do any of that,
so you can't do any of that. We think that's
an OK concession. There's no dynamism. No more flags. No more recomposing your
application at runtime. You still have all
the same conditionals, below provider methods,
and above your components. But we don't let you
retool how the framework is behaving dynamically. That seemed also kind of
to be it OK concession, because it was causing a lot
of people a lot of confusion. And the biggest issue
that we are finding, is that because of
these constraints there is no automated
migration path from Guice. You can't say, oh swap
out the set of imports, for this other set of
imports, and all of a sudden your application
becomes a lot faster. And as I mentioned, we
have a lot of Guice code. So if you want the
benefits of Dagger 2, you're probably going to have
to do a little bit of work to migrate your app
from one to the other. And again, that's
it's a concession, but I think that
it's probably fine, specifically as we software
engineering community at large can't seem to spawn
new apps faster than we know what to do with. So if we only target
new apps, I think that's probably not
half bad either. So what's the status? Where are we? Can we put this in your hands? This very specifically, you
guys might have noticed, wasn't a tutorial. I didn't show you
how to do it, I showed you why we
do it, because how to do it isn't quite there yet. We are roughly speaking,
feature complete. If everything goes
well, it runs. And like I said, we've seen
this running on something very like production traffic. But we have bugs. We have bugs that
we have to squash. We have error messages that
we need to make a lot better. In fact my partner in
crime over there and I spend probably a
good hour and a half locked in a very hot
to room debating over just what we're
going to show you when you have a cycle
in your dependencies. We are working very hard, and
taking that very seriously, because we don't want to
give you something where your first experience
with it is, I made a mistake in
the thing blew up. We have some preliminary
performance tests that I mentioned. And we are developing
a migration guide, not from Guice,
but from Dagger 1. We do fully expect to
support, I have Dagger 1, now I want Dagger 2. They're similar
enough that I think we should be able to give you
a pretty clear path forward. So that's where we're at. And we expect, certainly, I will
promise that we will have this in your hands, in the open
source projects hands, certainly within the
quarter in some form. But you can go
check out the code, build it yourself, poke around. But when things break, it's
sort of your own risk right now. Resources for all of this. We've got the Dagger 1 page, the
Dagger 2 Design Doc, the Dagger 2 Code. We have some spin-off
projects that are probably worth taking a
look at, if this is the sort of thing
that interests you. Compile testing
was a big effort, where we made some tools for
testing annotation processors. If you ever write one, I highly
recommend looking at that. And auto-commonism,
common libraries for annotation processors.
Can someone point me to a resource that explains why I want to be using dependency injection in my code? It seems like a lot of work just to be able to code against interfaces rather than concrete implementations.
I understand the premise behind it. You're able to quickly swap implementations without refactoring code, which is useful for automated unit testing. But if i'm not using unit testing, what are some other practical applications for DI?
Might not answer all the questions here but I made the simplest demo I could that uses dagger, retrofit, eventbus and butterknife to give a few people at work an idea of how dagger works. Only think thats unique to other setups is the idea of having Tasks (pretty much just runnables)
Its my current show new devs example
https://github.com/digitalbuddha/DaggerDemo
This is going to be great. The lack of flexibility may make it unusable to some, but an Injection Framework with no reflection is huge.
I'm very much on the side of "The simplest solution is normally the most correct", so I'm all for it.
Am I understanding this correctly, you will need do define your injectable classes twice? Once in the module and once in the component?
And how is this gonna affect bootstrapping?
Can you actually download / use Dagger 2.0? Is it released yet?
Only see 1.2 @ http://mvnrepository.com/artifact/com.squareup.dagger/dagger
A kind of random/semi-related question:
he mentions that many other forms of DI have the con of untraceable application flow (or difficult to trace). I've experienced this firsthand when I was given a medium sized codebase for a registration server. My question is, what is the best way to go about "tracing" or learning how things flow for an application that uses DI?
Looking good, but I do wonder where field injection fits in to the @Component API if ObjectGraph is going away (particularly important on Android, where we often can't use constructor injection).
For anybody that wants to see an actual example android app that utilizes the Dagger 2 framework, take a look here: https://github.com/cgruber/u2020/tree/dagger2
It's a fork of /u/JakeWharton's previous android example that utilized the Dagger 1 framework.
Both cgruber and JakeWharton are contributors to both dagger 1 and 2.