I recently saw mention that Google had released Guice, their internal dependency injection framework. As an experiment, I ported a few parts of the web application I’m working on for work to use it. I’ve never used dependency injection before, and so it was a chance to learn about it too. The executive summary is that it works great. Previously I had a giant factory class handling all the instantiation of singletons (mostly which handle talking to the database), so I would do stuff like:
Configuration.getObjectRegistry().getNoticeHandler()
This worked well enough, but all the static references made testing hard, I couldn’t substitute in my own fake classes at all.
Now, for the classes that I’ve ported to use Guice, in my test case I can create a module that makes it load a bunch of fake classes to simulate what the real ones would do, so I only need to test the one class that I’m looking at. It also means I don’t have to set up database entries for everything that might get touched. I just add a line to the module like:
bind(INoticeHandler.class).to(FakeNoticeHandler.class).in(Scopes.SINGLETON);
It also means that I'm on my way to removing the giant factory class that is a significant source of ugly in the application.
The only gotcha I had was that I couldn't get it to inject into the servlet objects automatically. This is because you have no control over their instantiation, the application server handles that all itself. The solution to this is for the class that catches the application loading to store the Injector
instance in the ServletContext
, and have each servlet object load that and inject the dependencies into itself on init:
@Override public void init(ServletConfig config) throws ServletException { super.init(config); ServletContext context = config.getServletContext(); Injector injector = (Injector) context.getAttribute("injector"); if (injector == null) { throw new UnavailableException("The Guice injector could not be " + "found in the ServletContext. It is expected to be named " + "'injector'."); } injector.injectMembers(this); }
if every servlet extends the abstract class that contains this (and this extends HttpServlet
), then it all magically just works.
Over time I plan to port the rest of the application to use this, but probably just as I’m working on those classes. In the documentation it says:
Dependency injection is viral. If you’re refactoring an existing code base with a lot of static methods, you may start to feel like you’re pulling a never-ending thread. This is a Good Thing. It means dependency injection is making your code more flexible and testable.
This is definitely the case, it would be easy to start and just keep following the thread of dependencies until the whole thing was done. However, I’m avoiding that now just due to a) being scared to refactor the whole damn thing at once, and b) I should be spending time on bug fixing and critical features right now. But, it probably won’t be too long before the whole thing is done, as a side-effect of testing.
Nice post.
I wonder why Guice was needed to make you step into DI ? You could have done the same thing with Spring for a long time.
Perhaps Spring *is* bloated as some guys are telling us – is its learning curce too steep for beginners?
The reason why I used Guice is twofold (although only one of these applied at the time I started with it).
The primary reason is because I didn’t really know about Spring. I knew it existed, but hadn’t looked into what it did or anything like that. However, when Guice was announced, I decided that I should have a look and see what it was about. Had I found Spring earlier, I may have considered using that.
The secondary reason (that I explained to my boss when talking about it) is that we use packers for a lot of our stuff, that take a class, and follow through the classes that that uses, and so on recursively. The impression I get is that they won’t work with Spring (its dependencies not being included in the Java code), but will work with Guice, provided you don’t try to use its implicit rules (which is fine, I’m happy with explicit binding, their implicit rules don’t match my class naming, which is along the lines of IInterface for the interface, and then MethodImplentation, e.g. SQLImplementation for something backed by a database). However Guice requires the import of classes that you’re going to use (with the one exception of the interface Service defaulting to the implementation ServiceImpl). So a packer should be able to cope with this happily.
A slightly cleaner approach is to move the Injector creation into a context listener. So it’s created there and stuffed into the ServletContext, and the base servlet can just get it instead of having to get/check for null/set.
Oh, it’s not very clear there, but that’s exactly what I do. The null check is simply a fail-(fairly)-fast way of telling if something is wrong. No setting actually happens in the servlet classes, it’s all in a context listener.
Every good thing can turn bad if you overuse. Actually usually DI is very nice for you to config live component and test/mock component clean and easy. However, DI also mean lack of call tracking and depend of unique ID. If you over use DI will make the code hard to track and also too many config files and hard to understand.