- What are Design Patterns?
- Inversion of Control
- Dependency Injection
- Dependency Injection in Spring Framework
A lot of ground will be covered in these posts, but it will all center around Spring Framework. I will show you the most important design patterns used in Spring, thanks to which you will understand this framework better.
- Java knowledge
- Basic Spring knowledge
- Object-Oriented Programming
What are Design Patterns?
Design patterns are solutions to common problems. You can say that Design Patterns are guidelines on how to tackle problems in certain situations. There are three types of patterns:
Creational Design Patterns
Creational patterns are focused on how to instantiate an object or group of related objects.
Structural Design Patterns
Structural patterns are mostly concerned with object composition or in other words how the entities can use each other.
Behavioral Design Patterns
Behavioral Patterns are concerned with providing solutions regarding object interaction - how they communicate, how are some dependent on others.
Inversion of Control
Inversion of Control is one of the most important patterns that is used by Spring. This is the main principle of Spring. The main advantages of IoC are:
- Decreases coupling
- Improves testability
- Greater modularity of a program
How it works? The control of application objects is transferred to a container or framework. A central container constructs and manages all objects references. When other components within the application need an object - IoC container hands it to them.
How it works in Spring Framework? In Spring, the IoC container manages all of the application dependencies. Instead of writing “new Class()” on your code, the object is created by someone else. This ‘someone else’ is referred to as the IoC Container.
In addition to creating new objects, Spring IoC container manages their entire life cycle. That includes creating objects, destroying them and invoking certain methods of the object (callbacks). So, what are the benefits of Spring IoC? Firstly, it reduces noise in the code (Imagine creating a JDBC connection object in dozens of classes). It also reduces object coupling, because you are handed a dependency into your object, you are not as tightly coupled to it (you actually don`t need to know how it is constructed).
Dependency injection is a software design pattern that implements inversion of control for resolving dependencies.
Let’s start with a simple example:
Writing a unit test for the
PrintOnScreen class is non-trivial. Additionally, in the
PrintOnScreen class there is initialized the printer field with a specific implementation. If we would like to change the implementation of the Screen interface, it will be necessary to modify the code of the
MainScreen class. We can get rid of these disadvantages using the DI approach. We just need to modify the
MainScreen class to accept the
Screen , for example - injected by the constructor:
This implementation of the
PrintOnScreen class will be easier to test. The test in Mockito might look like this:
By using DI, the
PrintOnScreen class has become easier to test, but initialization is more complicated. Fortunately, Spring Framework allows us to use the benefits of DI in an easier way.
Dependency Injection in Spring Framework
One way to create a Spring bean is to add the
@Component annotation to the class that you want to become a bean. The container will create an instance of this class and inject the required dependencies into it.
There are 3 ways to inject dependencies:
- Constructor Dependency Injection:
- Setter Dependency Injection:
- Field Dependency Injection:
Which way of dependency injection is the best?
Injection through the field seems to be the most convenient, but this approach has several disadvantages:
- You can not add a final modifier to such a field, which makes our class mutable.
- This makes it difficult to initialize dependencies in tests.
- If we use injecting by the constructor and our application will have a cyclic relationship between the beans, we’ll get an exception just after starting the application. If injected through the field, the exception will not fly and the application will work.
Spring developers recommend injecting by the constructor.
What if more than one bean implements the same interface?
Sometimes several classes - beanes implements the same interface:
Then we need to indicate the implementation that is to be injected. We have several options:
- Using the bean name as the name of the method parameter
If we do not give the name of the bean in the annotation parameter, it will be the name of the method by default.
- @Qualifier annotation
- We can mark one of the beans with @Primary annotation. If there is no qualifier for injection, this implementation will be selected by default.
In this post, I demonstrated basic examples of Dependency Injection in the Spring Framework. I hope you can see how simple Spring makes Dependency Injection.
If you liked it, I invite you to read the next part in which I described the most popular creational design patterns: Design Patterns used in Spring Framework Part 2 - Creational Patterns.