If you don’t feel confident about the meaning of dependency injection or IoC, better start with this article. I won’t tell you about absolutely all dependency injection types, but those that are possible while using Spring framework.
Spring documentation strictly defines only two types of injection: constructor and setter injection.
However, there are more ways to inject a dependency like a field injection, lookup method injection. To understand their difference, see detailed examples, learn the advantages or disadvantages of each approach — continue reading. Warning — long read! :)
For each dependency injection type examples will include pure XML config (if possible) in the first place to present the idea and only after that XML + Annotations config to demonstrate the simpler and more common way of dependencies configuration. For the sake of brevity, I won’t show Java configuration examples.
Constructor Injection —enforcing immutability
This is the most straightforward and recommended way of dependency injection. A dependent class has a constructor, where all dependencies are set, they will be provided by Spring container according to XML, Java or annotation based configurations.
Let’s start with an example:
We have here 2 independent services and 1 dependent. Dependencies are set in the constructor only, to run it we initialize the container providing it with our configuration contained in spring.xml. Time to see the configuration as well.
So, we’ve just declared all our 3 services as Spring beans, the last one references first 2 as constructor arguments. However, it seems quite wordy, is it required to write so much configuration? Luckily not, we can use “autowiring” — let Spring guess what should be injected where with a minimum of configuration effort from our side. To help Spring orient in our code let’s mark our services with annotation @Service and constructor where injection happens with @Autowired or @Inject. @Autowired belongs to Spring framework, @Inject to JSR-330 annotations collection, both will work for our example.
Alright, it is done. But should we change something in spring.xml? Definitely!
Here we’ve just indicated in which package our services are going to be. Isn’t it simple? At the same time, there are some drawbacks in this approach in comparison to pure XML way. In XML configuration we were referencing exactly the bean that should be injected, in the new auto wiring approach Spring tries to find matching beans in context and sometimes requires our help. For example, there are 2 classes (beans) implementing one interface and we want to have the interface autowired somewhere in our code, to make it work in Spring we should indicate which of these 2 beans is qualified for us in this particular case - by using @Qualifier annotation with the identifier of chosen implementation.
Spring provides a possibility to use autowiring even in pure XML approach. However, it is highly recommended to avoid this functionality.
One important detail — you can have many more constructors in your class, only one of them should qualify for dependency injection.
Advantages of constructor injection
- Constructed object is immutable and returned to the client in a fully initialized state.
- An issue with a growing amount of dependencies is immediately visible with this approach. More dependencies bigger the constructor.
- Can be combined with setter injection or field injection, constructor parameters indicate required dependencies, others — optional.
And the disadvantages
- No possibility to change object’s dependencies later — inflexibility.
- Higher chance to have circular dependencies, so-called chicken-and-egg scenario.
Setter injection — enjoy the mutability
To show this way of injection I’ll use the same code that was in examples before. As there won’t be that many changes from constructor injection for the sake of brevity only changed logical parts will be shown.
As the name of injection type implies we should have created setters for our dependencies first. A constructor is not required in this case. Let’s take a look at what should be done in XML configuration.
So, quite straightforward, isn’t it? Let me demonstrate the annotation based version for better look and feel.
Here we’ve just put the @Autowired annotation on setters. Again, @Inject would work here as well without any issues.
Setters presence is required for this type of injection in the same way as a constructor is required for constructor injection.
While describing constructor injection advantages I mentioned that it can be combined easily with setter injection. The time to show it has come.
In our example, the service1 object is a mandatory dependency (final property) and is set only once during instantiation of DependentService instance. Meanwhile, service2 is optional, can contain null at first and its value may be changed any time after creation by calling setter. In the XML way, you would have to specify both property and constructor-arg tags inside the bean declaration.
- Flexibility in dependency resolution or object reconfiguration, it can be done anytime. Plus, this freedom solves the circular dependency issue of constructor injection.
- Null checks are required, because dependencies may not be set at the moment.
- Potentially more error-prone and less secure than constructor injection due to the possibility of overriding dependencies.
Field injection — nobody likes it?
This type of injection is possible only in the annotation based approach due to the fact that it is not really a new injection type — under the hood, Spring uses reflection to set these values.
Why did I mention this approach separately from other ways? Once I was asked — what type of injection do you use in the project, setter or constructor? After that question, I looked at the code and saw annotations over fields only, no setters, no constructors. I confess I was puzzled. Why is it mentioned so rare and even when mentioned people tend to say something like “don’t use it, no way”? Let’s discuss the advantages and disadvantages of this approach.
- Easy to use, no constructors or setters required
- Can be easily combined with the constructor and/or setter approach
- Less control over object instantiation. In order to instantiate the object of a class for a test, you will need either a Spring container configured or mock library — depends on the test you are writing.
- A number of dependencies can reach dozens until you notice that something went wrong in your design.
- No immutability — the same as for setter injection.
Lookup Method injection — what? why? when?
This injection type is used less frequently than all the others described above because of the special use case, namely the injection of dependency with a smaller lifetime. Let me explain it briefly.
By default, all beans in Spring are created as singletons, which means they will be created in a container once and the same object will be injected anywhere it is requested. However, sometimes, a different strategy is required, for example, each method call should be done from a fresh object. And now imagine that this short lifetime object is injected to the singleton object, will Spring refresh this dependency automatically with every invocation? No, the dependency will be still perceived as a singleton unless we indicate the existence of this special dependency type.
Returning back to practice, we have again 3 services, one of them is depending on others, service2 is the usual object which can be injected in DependentService by any of previously described dependency injection techniques, let’s say setter injection. The object of Service1 will be different, it can not be injected once, a new instance should be accessed with each call — let’s create a method that will provide this object and let Spring know about it.
We haven’t declared an object of Service1 as a usual dependency, instead, we’ve specified method which will be overridden by Spring framework in order to return the latest instance of Service1 class.
Method-provider of dependency doesn’t have to be abstract and protected, it can be public and contain implementation but keep in mind that it will be overridden in a subclass, created by Spring.
We are again in pure XML configuration example, so we will have to indicate first that service1 is an object with a short lifetime, in Spring terms we can use here prototype scope as it is smaller than a singleton. By lookup-method tag, we can indicate the name of the method, which will inject dependency.
The same can be done in an annotation based way.
Disadvantages and Advantages
It won’t be correct to compare this type of dependency injection with others as it has an absolutely different use case.
We went through 4 types of dependency injection implemented by Spring framework:
- Constructor injection — good, reliable and immutable, inject via one of the constructors. Possible to configure in: XML, XML+Annotations, Java, Java + Annotations.
- Setter injection — more flexible, mutable objects, injection via setters. Possible to configure in: XML, XML+Annotations, Java, Java + Annotations.
- Field injection — fast and convenient, coupling with IoC container. Possible to configure in XML+Annotations, Java + Annotations.
- Lookup method injection — totally different from others, used for injection dependency of smaller scope. Possible to configure in: XML, XML+Annotations, Java, Java + Annotations.
Despite constructor tends to be recommended way I would advise you to clarify your needs first, maybe setter of field injection would be more appropriate for your application. In any case, you can always mix different approaches and achieve your goals.