Introduction to Spring, or what to do if the whole project is @Autowired and @Component, but you don’t understand what it is
- Tutorial
I greet you, Habr!
This article will be useful to those who have already started to learn Java and even managed to achieve some success in understanding Java Core, and now I heard the word Spring. And, perhaps, not even once: knowledge of the Spring Framework, at least, appears in the descriptions of many vacancies for javists. This article will help you climb the very first step: to understand the general idea of such a popular framework.
Let's start from afar. There is such a thing as Inversion of Control, in Russian - Inversion of control, in abbreviated form - IoC. IoC is one of the principles that brings our code closer to looseness. IoC is the delegation of part of our responsibilities to an external component.
There are different implementations of the IoC approach, we are interested in one of them - Dependency Injection, dependency injection. What is this, the name speaks for itself, so I will try to disclose it using an example. We are writing an application that automates the operation of a chain of stores. There are classes Shop (store) and Seller (seller). The class Seller has a field of type Shop - the store in which the seller works. So we are faced with addiction: Seller is dependent on Shop. Now let's think about how the Shop object gets into the Seller object? There are options:
The two methods listed are the implementation of Dependency Injection. And finally, we got to the spring: it provides another way to inject dependencies.
Generally speaking, Spring is a very wide range of libraries for many occasions. There is Spring MVC for quickly creating web applications, and Spring Security for implementing authorization in the application, and Spring Data for working with databases and much more. But Spring IoC stands alone - this is the basic type of spring that implements the topic we are studying - dependency injection. Spring IoC deserves attention at the very beginning of the study of spring libraries for another reason. As you will see in the process of practical work with other types of spring, for all other springs Spring IoC is used as a framework.
We will begin our introduction to Spring IoC with the main term: bean. In the simplest words
Do you want more complicated words? And please:
As you already understood, in order for Shop to be able to implement Shop, Shop must become a bin. There are several ways to tell the application which objects have the proud right to be called beans, all of which lead us to the concept of ApplicationContext. ApplicationContext is the heart of spring. As a rule, it is created at the very beginning of the application ("rises") and controls the life cycle of the beans. Therefore, it is also called a bin container.
We are getting to the main thing. How do we need to rewrite our classes so that Spring IoC and its ApplicationContext servant substitute the Shop field for the Seller object? Like this:
Simply? Much easier! Elegantly? Quite. Here the following happened: the Component annotation told Spring that the class that it annotated is a bin. Annotation Autowired asked Spring to substitute a value in the field that it annotates. This operation is called inject. What exact value will be substituted? We'll talk about this a bit later, first, we will figure out how classes become beans in general.
We already know that at the beginning of the application, the guardian of all ApplicationContext beans must rise. He creates all bins at once. Almost all. The fact is that by default, any bean has the intraspring property scope in the value singleton. Intraspringovoe, since in the literal sense of the word he is not a singleton. It is the singleton for the spring: when the context is raised, Spring will create exactly one bin object from the specified class. If you want to change this behavior - please, Spring allows you to control the creation time of the bean and their number for one class, but now is not about that.
So, when raising ApplicationContext all bins are created. Let's find out exactly where the context lives and most importantly: how it determines which classes to create bins from. There are several options, for simplicity, we will talk about one of them: configuration using the xml file. Here is an example of it:
This file demonstrates how to create beans in two ways. The first one, let’s say, is manual. You see, there is a bean tag with an indication of the class. This is the bean. From everything that is registered in this file with a bean tag, beans will be created.
The second way is less verbose. Remember, over the classes we put the Component annotation. Of all the classes annotated with this annotation, beans will be created. Thanks to this line from the xml file:
She tells spring: scan the entire main package and from all that the Component annotation (or other annotations that are components of Component) will stand on, create bins. Compact, isn't it? We just say which packages contain the classes from which to create the beans, and annotate these classes.
You can raise the context using the xml file with the following line of code:
where beans.xml is the path to the xml nickname discussed above.
With the creation of beans figured out. How does Spring fill the Shop field when creating a Seller? When raising context, a bin object of class Shop is created. A bin object of the Seller class is also created; it is also annotated by Component. It has a Shop type annotated by Autowired. Autowired annotation tells spring: you need to inject the bin into this field. In our case, we have only one bin suitable for this role, that is, the type of which matches the type of the field: this is a bin - an instance of the Shop class. It will be injected into the Seller object, as required. I understand that now the questions have climbed like worms: what will happen if Spring does not find the desired bean, or finds several suitable ones (especially considering that you can also inject via the interface, and not by class). Spring is smart, but it requires the same from us. We need to either have exactly one bin in the system,
Note that Seller is also a bin. If it was not a bin, but created via new, then nothing would automatically inject into it.
Perhaps now you are thinking why all these difficulties are necessary. But imagine that our application is not of 2 classes, but several orders of magnitude larger and dependency management is no longer the most trivial task.
Perhaps now you think how beautiful, simple and concise Spring allows you to implement dependencies. But imagine that something went wrong and you need to debug the application. And everything is becoming not so simple ... A
couple of hints at last:
This is a legal way to get a bin, although in modern realities this is usually not done. But for a case study you can.
Spring IoC contains tremendous opportunities for creating, configuring, and injecting beans. We examined a tiny part of the tip of the iceberg, one of the ways to work with spring, but I hope that we were able to take a sharp look at it and get a general idea of what is happening.
This article will be useful to those who have already started to learn Java and even managed to achieve some success in understanding Java Core, and now I heard the word Spring. And, perhaps, not even once: knowledge of the Spring Framework, at least, appears in the descriptions of many vacancies for javists. This article will help you climb the very first step: to understand the general idea of such a popular framework.
Let's start from afar. There is such a thing as Inversion of Control, in Russian - Inversion of control, in abbreviated form - IoC. IoC is one of the principles that brings our code closer to looseness. IoC is the delegation of part of our responsibilities to an external component.
There are different implementations of the IoC approach, we are interested in one of them - Dependency Injection, dependency injection. What is this, the name speaks for itself, so I will try to disclose it using an example. We are writing an application that automates the operation of a chain of stores. There are classes Shop (store) and Seller (seller). The class Seller has a field of type Shop - the store in which the seller works. So we are faced with addiction: Seller is dependent on Shop. Now let's think about how the Shop object gets into the Seller object? There are options:
- Implement it through the designer and immediately, when creating the seller, specify the store in which he works:
public class Seller {
private Shop shop;
public Seller(Shop shop) {
this.shop = shop;
}
}
- Create a setter and, using his call, set the store for the seller:
public class Seller {
private Shop shop;
public void setShop(Shop shop) {
this.shop = shop;
}
}
The two methods listed are the implementation of Dependency Injection. And finally, we got to the spring: it provides another way to inject dependencies.
Generally speaking, Spring is a very wide range of libraries for many occasions. There is Spring MVC for quickly creating web applications, and Spring Security for implementing authorization in the application, and Spring Data for working with databases and much more. But Spring IoC stands alone - this is the basic type of spring that implements the topic we are studying - dependency injection. Spring IoC deserves attention at the very beginning of the study of spring libraries for another reason. As you will see in the process of practical work with other types of spring, for all other springs Spring IoC is used as a framework.
We will begin our introduction to Spring IoC with the main term: bean. In the simplest words
A bean is a class object created by Spring that can be embedded as a field value into another object.
Do you want more complicated words? And please:
A bean is a class object, which is a complete program element with a specific business function or an internal Spring function, whose life cycle is controlled by the bin container.
As you already understood, in order for Shop to be able to implement Shop, Shop must become a bin. There are several ways to tell the application which objects have the proud right to be called beans, all of which lead us to the concept of ApplicationContext. ApplicationContext is the heart of spring. As a rule, it is created at the very beginning of the application ("rises") and controls the life cycle of the beans. Therefore, it is also called a bin container.
We are getting to the main thing. How do we need to rewrite our classes so that Spring IoC and its ApplicationContext servant substitute the Shop field for the Seller object? Like this:
@Component
public class Shop {
}
@Component
public class Seller {
@Autowired
private Shop shop;
}
Simply? Much easier! Elegantly? Quite. Here the following happened: the Component annotation told Spring that the class that it annotated is a bin. Annotation Autowired asked Spring to substitute a value in the field that it annotates. This operation is called inject. What exact value will be substituted? We'll talk about this a bit later, first, we will figure out how classes become beans in general.
We already know that at the beginning of the application, the guardian of all ApplicationContext beans must rise. He creates all bins at once. Almost all. The fact is that by default, any bean has the intraspring property scope in the value singleton. Intraspringovoe, since in the literal sense of the word he is not a singleton. It is the singleton for the spring: when the context is raised, Spring will create exactly one bin object from the specified class. If you want to change this behavior - please, Spring allows you to control the creation time of the bean and their number for one class, but now is not about that.
So, when raising ApplicationContext all bins are created. Let's find out exactly where the context lives and most importantly: how it determines which classes to create bins from. There are several options, for simplicity, we will talk about one of them: configuration using the xml file. Here is an example of it:
This file demonstrates how to create beans in two ways. The first one, let’s say, is manual. You see, there is a bean tag with an indication of the class. This is the bean. From everything that is registered in this file with a bean tag, beans will be created.
The second way is less verbose. Remember, over the classes we put the Component annotation. Of all the classes annotated with this annotation, beans will be created. Thanks to this line from the xml file:
She tells spring: scan the entire main package and from all that the Component annotation (or other annotations that are components of Component) will stand on, create bins. Compact, isn't it? We just say which packages contain the classes from which to create the beans, and annotate these classes.
You can raise the context using the xml file with the following line of code:
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
where beans.xml is the path to the xml nickname discussed above.
With the creation of beans figured out. How does Spring fill the Shop field when creating a Seller? When raising context, a bin object of class Shop is created. A bin object of the Seller class is also created; it is also annotated by Component. It has a Shop type annotated by Autowired. Autowired annotation tells spring: you need to inject the bin into this field. In our case, we have only one bin suitable for this role, that is, the type of which matches the type of the field: this is a bin - an instance of the Shop class. It will be injected into the Seller object, as required. I understand that now the questions have climbed like worms: what will happen if Spring does not find the desired bean, or finds several suitable ones (especially considering that you can also inject via the interface, and not by class). Spring is smart, but it requires the same from us. We need to either have exactly one bin in the system,
Note that Seller is also a bin. If it was not a bin, but created via new, then nothing would automatically inject into it.
Perhaps now you are thinking why all these difficulties are necessary. But imagine that our application is not of 2 classes, but several orders of magnitude larger and dependency management is no longer the most trivial task.
Perhaps now you think how beautiful, simple and concise Spring allows you to implement dependencies. But imagine that something went wrong and you need to debug the application. And everything is becoming not so simple ... A
couple of hints at last:
- If you have implemented the project and are now at a loss how to get a bin from the spring to look at it, do this:
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Seller seller = (Seller) context.getBean(Seller.class);
This is a legal way to get a bin, although in modern realities this is usually not done. But for a case study you can.
- Since Spring is a framework, you need to include it in your project. I create an application using maven and add spring-core and spring-context dependencies to the pom.xml file.
Spring IoC contains tremendous opportunities for creating, configuring, and injecting beans. We examined a tiny part of the tip of the iceberg, one of the ways to work with spring, but I hope that we were able to take a sharp look at it and get a general idea of what is happening.