I understand benefits of dependency injection itself. Let\'s take Spring for instance. I also understand benefits of other Spring featureslike AOP, helpers of different kind
This is a bit of a loaded question, but I tend to agree that huge amounts of xml configuration doesn't really amount to much benefit. I like my applications to be as light on dependencies as possible, including the hefty frameworks.
They simplify the code a lot of the times, but they also have an overhead in complexity that makes tracking down problems rather difficult (I have seen such problems first hand, and straight Java I would be a lot more comfortable dealing with).
I guess it depends on style a bit, and what you are comfortable with... do you like to fly your own solution and have the benefit of knowing it inside out, or bank on existing solutions that may prove difficult when the configuration isn't just right? It's all a tradeoff.
However, XML configuration is a bit of a pet peeve of mine... I try to avoid it at all costs.
The reason for using a DI container are that you don't have to have a billion properties pre-configured in your code that are simply getters and setters. Do you really want to hardcode all those with new X()? Sure, you can have a default, but the DI container allows the creation of singletons which is extremely easy and allows you to focus on the details of the code, not the miscellaneous task of initializing it.
For example, Spring allows you to implement the InitializingBean interface and add an afterPropertiesSet method (you may also specify an "init-method" to avoid coupling your code to Spring). These methods will allow you to ensure that any interface specified as a field in your class instance is configured correctly upon startup, and then you no longer have to null-check your getters and setters (assuming you do allow your singletons to remain thread-safe).
Furthermore, it is much easier to do complex initializations with a DI container instead of doing them yourself. For instance, I assist with using XFire (not CeltiXFire, we only use Java 1.4). The app used Spring, but it unfortunately used XFire's services.xml configuration mechanism. When a Collection of elements needed to declare that it had ZERO or more instances instead of ONE or more instances, I had to override some of the provided XFire code for this particular service.
There are certain XFire defaults defined in its Spring beans schema. So, if we were using Spring to configure the services, the beans could have been used. Instead, what happened was that I had to supply an instance of a specific class in the services.xml file instead of using the beans. To do this, I needed to provide the constructor and set up the references declared in the XFire configuration. The real change that I needed to make required that I overload a single class.
But, thanks to the services.xml file, I had to create four new classes, setting their defaults according to their defaults in the Spring configuration files in their constructors. If we had been able to use the Spring configuration, I could have just stated:
<bean id="base" parent="RootXFireBean">
<property name="secondProperty" ref="secondBean" />
</bean>
<bean id="secondBean" parent="secondaryXFireBean">
<property name="firstProperty" ref="thirdBean" />
</bean>
<bean id="thirdBean" parent="thirdXFireBean">
<property name="secondProperty" ref="myNewBean" />
</bean>
<bean id="myNewBean" class="WowItsActuallyTheCodeThatChanged" />
Instead, it looked more like this:
public class TheFirstPointlessClass extends SomeXFireClass {
public TheFirstPointlessClass() {
setFirstProperty(new TheSecondPointlessClass());
setSecondProperty(new TheThingThatWasHereBefore());
}
}
public class TheSecondPointlessClass extends YetAnotherXFireClass {
public TheSecondPointlessClass() {
setFirstProperty(TheThirdPointlessClass());
}
}
public class TheThirdPointlessClass extends GeeAnotherXFireClass {
public TheThirdPointlessClass() {
setFirstProperty(new AnotherThingThatWasHereBefore());
setSecondProperty(new WowItsActuallyTheCodeThatChanged());
}
}
public class WowItsActuallyTheCodeThatChanged extends TheXFireClassIActuallyCareAbout {
public WowItsActuallyTheCodeThatChanged() {
}
public overrideTheMethod(Object[] arguments) {
//Do overridden stuff
}
}
So the net result is that four additional, mostly pointless Java classes had to be added to the codebase to achieve the affect that one additional class and some simple dependency container information achieved. This isn't the "exception that proves the rule", this IS the rule...handling quirks in code is much cleaner when the properties are already provided in a DI container and you're simply changing them to suit a special situation, which happens more often than not.
I have your answer
There are obviously trade offs in each approach, but externalized XML configuration files are useful for enterprise development in which build systems are used to compile the code and not your IDE. Using the build system, you may want to inject certain values into your code - for example the version of the build (which could be painful to have to update manually each time you compile). The pain is greater when your build system pulls code off of some version control system. Modifying simple values at compile time would require you to change a file, commit it, compile, and then revert each time for each change. These aren't changes that you want to commit into your version control.
Other useful use cases regarding the build system and external configs:
Update: All the above examples were on things that didn't necessarily require dependencies on classes. But you can easily build up cases where both a complex object and automation is necessary - for example:
Ease of combining partial configurations into a final complete configuration.
For example, in web applications, the model, view and controllers are typically specified in separate configuration files. Use the declarative approach, you can load, for example:
UI-context.xml
Model-context.xml
Controller-context.xml
Or load with a different UI and a few extra controllers:
AlternateUI-context.xml
Model-context.xml
Controller-context.xml
ControllerAdditions-context.xml
To do the same in code requires an infrastructure for combining partial configurations. Not impossible to do in code, but certainly easier to do using an IoC framework.