Java – What complexity do DI frameworks add

dependency-injectionframeworksgoogle-guicejava

The currently most upvoted answer to a very recent question states that

DI containers are an "enterprise software" pattern, used when the object graph is very large and complex. I suspect that 95% of applications do not require it.

which is something I strongly disagree with. Maybe I've got the terminology wrong, but for me DI framework means just "something wiring my objects together". Am I missing something?

I'm using Guice even for really tiny projects (like 10 classes) in order to simplify them. Sure, it's a 400 kB JAR file, but this isn't what I'd care about. For a small project I hardly ever need any configuration and the only "overhead" is adding the @Inject annotation.

So I really wonder, what added complexity do DI frameworks cause?

Update addressing the answers

In a 82 class project, I have

  • 32 @Inject annotations
  • 15 @Singleton and 1 @ProvidedBy annotations
  • 4 Providers (all of which I'd need also without DI as they're my factories)
  • 1 Module containing a single line
  • 0 lines XML !!!

That's all. Sure, it's small project, but exactly this was my point. The additional work was a few words rather than lines.

I'm using solely constructor injection in order to get immutable "easygoing" objects. Whenever a new dependency appears, I add a final field, let Lombok's RequiredArgsConstructor take care of the declaration, and let Guice take care of calling it properly.

Best Answer

There are a couple of compromises that are to be made when using DI frameworks as far as I can see.

The most worrying for me is that your application code is usually spread between a main programming language (like Java) and XML/JSON configuration code (it is code). This means that in the case of problems with your application you need to look in two places. Often the configuration code is not easily related to the main application code.

Of course the configuration is also outside the bounds of what the compiler (and often the IDE) can check for you, meaning that it is much easier to make mistakes in this configuration than if you were writing a main class that handled the wiring. In effect this means that wiring issues are pushed from being compile-time issues to being runtime issues.

As well as splitting the application, using DI Frameworks also often mean that you use @Inject or similar within your code, instead of traditional DI techniques (CTOR interface injection in Java/C++, template injection in C++). The downside of this is that you must then use a DI framework with the application. An alternative would be to design your application without expecting a DI framework and then allow the DI framework to re-use the traditional CTOR/setter injection of your classes.

The downside to the traditional injection mechanism comes when a complex and properly encapsulation classes demands a number of other relatively complex injections at CTOR time. This is usually solved by incorporating a Builder, but it can be a pain.

EDIT:

The lads below have mentioned that Guice does not need a separate XML/JSON config. My answer really applies to my own usage of Spring.