DAGGER 2 - A New Type of dependency injection

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

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?

πŸ‘οΈŽ︎ 11 πŸ‘€οΈŽ︎ u/IWantToBeAProducer πŸ“…οΈŽ︎ Aug 22 2014 πŸ—«︎ replies

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

πŸ‘οΈŽ︎ 8 πŸ‘€οΈŽ︎ u/prlmike πŸ“…οΈŽ︎ Aug 22 2014 πŸ—«︎ replies

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.

πŸ‘οΈŽ︎ 4 πŸ‘€οΈŽ︎ u/Exallium πŸ“…οΈŽ︎ Aug 22 2014 πŸ—«︎ replies

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?

πŸ‘οΈŽ︎ 3 πŸ‘€οΈŽ︎ u/cypressious πŸ“…οΈŽ︎ Aug 22 2014 πŸ—«︎ replies

Can you actually download / use Dagger 2.0? Is it released yet?

Only see 1.2 @ http://mvnrepository.com/artifact/com.squareup.dagger/dagger

πŸ‘οΈŽ︎ 2 πŸ‘€οΈŽ︎ u/tidderkrow πŸ“…οΈŽ︎ Aug 22 2014 πŸ—«︎ replies

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?

πŸ‘οΈŽ︎ 1 πŸ‘€οΈŽ︎ u/samkxu πŸ“…οΈŽ︎ Aug 23 2014 πŸ—«︎ replies

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).

πŸ‘οΈŽ︎ 1 πŸ‘€οΈŽ︎ u/Tarenius πŸ“…οΈŽ︎ Aug 23 2014 πŸ—«︎ replies

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.

πŸ‘οΈŽ︎ 1 πŸ‘€οΈŽ︎ u/KurtLovesCode πŸ“…οΈŽ︎ Dec 18 2014 πŸ—«︎ replies
Captions
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.
Info
Channel: Google Developers
Views: 216,978
Rating: undefined out of 5
Keywords: Dagger2, java, NYC, live, petele, product: other, fullname: other, Location: other, Team: Other, Type: Live Event, GDS: Full Production
Id: oK_XtfXPkqw
Channel Id: undefined
Length: 40min 15sec (2415 seconds)
Published: Thu Aug 21 2014
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.