DI is an abbreviation for Dependency Injection, which is often translated as "Dependency Injection" in Japanese.
When I first came into contact with Spring when I was a new graduate, the first thing that came to my mind was the question mark. The connection with the concept of DI, DI container, and annotation in Spring didn't come to my mind.
This time, for myself as a beginner, I would like to briefly summarize the concept of DI and DI in Spring while organizing it in my own way.
I think it's easier to understand what a dependency is in the source code.
Main.java
public class Main {
public int hoge() {
var sub = new Sub();
return sub.calculate();
}
}
In the hoge method of Main class, an instance of Sub class is created and used. When there is such a Main class, it is expressed as "Main class depends on Sub class".
I think it's relatively easy to understand about addiction.
In the previous example, an instance of the Sub class was created internally, but creating an instance externally and passing it to the class that uses it is called injection.
Here, an example using a constructor (constructor injection) is shown.
Main.java
public class Main {
private Sub sub;
public int hoge() {
return sub.calculate();
}
//Constructor injection
public Main(Sub sub) {
this.sub = sub;
}
}
Earlier, we created an instance of the Sub class in the hoge method, but in this example, the instance is received from the constructor.
By doing this, when you actually use the Main class,
var sub = new Sub();
var main = new Main(sub);
In the form of, the instance of Sub class is passed from outside the Main class.
The benefits of this will be described later.
When you hear the word container, you may think of Docker in modern times, but the container in DI container is different from the container used in the context such as Docker.
If you write it in one word without fear of misunderstanding, the DI container acts as a "container that manages the instance to be injected".
If you try to use the Main class without using the DI container as before, you will need an instance of the Sub class each time you use it.
If you use a DI container, it will manage this instance, so you don't have to create an instance each time.
In Spring, by specifying a class with annotations, you can specify an instance of that class as a target to be managed by the DI container.
Specifically, the annotations are as follows.
@Controller
@Service
@Repository
@Bean
By the way, the instance managed by DI container can be obtained by using getBean method of ApplicationContext class.
In Spring, you can also set the scope of how long the DI container manages the instance.
The scopes that can be set are as follows, and the Scope annotation is used when setting.
The default of Spring is singleton, but by changing the setting, it is possible to destroy and reconfigure the instance managed by the container, for example, in session units.
One thing that beginners tend to do is to give the class a field that can change, despite the singleton. An example is shown below.
hogeService
@Service
public class hogeService {
private String result;
public int fuga(String suffix) {
result = "test" + suffix;
return result;
}
}
Since hogeService is annotated with Service and no scope is set, the scope is singleton.
As a result, you run the risk of changing the result field in another session, which makes it less thread-safe.
By making it a variable instead of a class field as shown below, it becomes a thread-safe implementation.
hogeService
@Service
public class hogeService {
public int fuga(String suffix) {
String result = "test" + suffix;
return result;
}
}
In Spring, you can DI by using Autowired annotation.
There are three injection methods, including the constructor injection mentioned earlier.
--Field injection --Setter injection --Constructor injection
Specifically, each is like this.
Main.java
public class Main {
//Field injection
@Autowired
private Sub sub;
public int hoge() {
return sub.calculate();
}
}
Main.java
public class Main {
private Sub sub;
public int hoge() {
return sub.calculate();
}
//Setter injection
@Autowired
public setSub(Sub sub) {
this.sub = sub;
}
}
Main.java
public class Main {
private Sub sub;
public int hoge() {
return sub.calculate();
}
//Constructor injection
@Autowired
public Main(Sub sub) {
this.sub = sub;
}
}
By annotating Autowired, it will inject instances from the DI container into the arguments of fields, methods and constructors.
By the way, when I inject myself, I write it using the RequiredArgsConstructor annotation of Lombok in the library, as described in the article here.
A common benefit is that unit tests are easier to write, I think this is true.
Here is the example at the beginning again.
Main.java
public class Main {
public int hoge() {
var sub = new Sub();
return sub.calculate();
}
}
If the calculate method of this Sub class needed to access the DB, unit testing of the Main class would be very difficult.
However, you can test it by mocking the instance you inject with DI. By the way, I use Mockito when using mock.
Finally, the following is a summary of DI and DI containers in one word.
DI: Passing an instance of a class that depends on a class from the outside DI container: A container that manages the instance to be injected
Using these has the advantage of making it easier to write tests and manage scope.
That's all for the content. We hope you find this article useful. Thank you for reading for me until the end.
Official documentation Core Technologies
Recommended Posts