Dependency Injection is great to design testable and modular apps, but whether you use Toothpick, Dagger or any other library, there are some tricky problems that come with DI. They can be hard to detect and quality and static code analysis tools (like pmd, findbugs, etc.) can’t help you to solve them.
At Groupon, we ❤️ Open Source software, and we decided to release a tool we developed internally. In this article we introduce Dependency Injection checks that will help you solve some issues that are common to all JSR 330 DI libs.
As code complexity grows, it can be difficult to track all of the dependencies that a particular class uses. This problem is even more complicated when we take inheritance into account. If multiple classes in a hierarchy inject the same dependency, it is perfectly valid from the language perspective, but it leads to slower runtime as you create the dependency subtree multiple times and your dependencies can get out of control. Let’s take a closer look at this issue we call a “duplicate injection”, and how we can use the new ‘Dependency Injection Checks’ library to automatically validate our design:
You’re writing a component “Foo” that uses an util class called “Waldo”, so you inject “Waldo” in that class. Now this class “Foo” in time is extended by “Bar” and “Baz”. Your project evolves and now you have another class “Qux” extending “Bar”.
There’s a new requirement in “Qux” to use “Waldo” for something, but by this time you already forgot all of the injected members of “Foo” so you inject the “Waldo” in “Qux”.
So now you have “Waldo” as a member of both “Foo” and “Qux”, and this is perfectly valid, but it’s not a good design since you can change the visibility of “Waldo” from “Foo” to “protected” and use it in “Qux” as well.
There is no tool to catch these sort of errors, and they creep into the cleanest of codebases.
Dependency Injection Checks Open Source library is an annotation processor that detects common DI related issues (like the one described above). When a duplicate injection is found, compilation fails, pointing you to the right place in the codebase to fix your issue.
The library uses annotation processing to build up an internal model of the hierarchies that use the @javax.inject.Inject annotation (JSR 330). It then checks to see if an injection is found twice in a hierarchy and records the place where this mistake occurs. Once all the injections are processed it checks for errors and gives the compiler a non successful exit code if one is found.
We designed the library to extend it and add more checks related to dependency injection and we have a plan to add some in the future, like making certain classes forbidden for injection.
You can check it out here:
Feel free to use it in your projects, share it with other projects that might benefit an automated check like this.
We would be happy to get your feedback in this article, and invite every one to contribute with GitHub issues or even pull requests to our new project.