Greetings to you, Habr!
This article will be useful to those who have already begun to learn Java and even managed to achieve some success in understanding Java Core, and now I heard the word Spring. And, perhaps, even more than once: knowledge of the Spring Framework, at least, appears in the descriptions of many vacancies for javista. 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, abbreviated as IoC. IoC is one of the principles that brings our code closer to loose coupling. 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 it is, the name speaks for itself, so I will try to reveal it with an example. We are writing an application that automates the work of a chain of stores. There are classes Shop (store) and Seller (seller). The class Seller has a field of type Shop - the shop in which the seller works. So we are faced with addiction: Seller depends on the Shop. Now let's think about how the Shop object gets to the Seller object? There are options:
- Implement it through the designer and immediately, when creating the seller, indicate the store in which he works:
public class Seller { private Shop shop; public Seller(Shop shop) { this.shop = shop; } }
- Create a setter and use it to install a shop to 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 are getting close to the spring: it provides another way to introduce dependencies.
Generally speaking, Spring is a very wide range of libraries for many occasions. There is also Spring MVC for quickly creating web applications, and Spring Security for implementing authorization in the application, and Spring Data for working with databases and a whole lot more. But there is a separate Spring IoC - this is a 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 the libraries of the spring for another reason. As you will see in the course of practical work with other types of spring, for all other springs, Spring IoC is used as a framework.
Acquaintance to Spring IoC we will begin with the main term: bin (English - bean). The simplest words
Bean is a class object created by Spring that can be embedded as a field value in another object.
Do you want more difficult words? And please:
Bin is a class object that represents a complete program element with a specific business function or an internal Spring function, the life cycle of which is managed by the bins container.
As you already understood, in order for the Seller to be able to implement the Shop, the 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 ApplicationContext.
ApplicationContext is the heart of the spring. As a rule, it is created at the very beginning of the application (“rises”) and manages the life cycle of the beans. Therefore, it is also called a container of bins.
We are getting to the main point. How do we need to rewrite our classes so that Spring IoC and its servant ApplicationContext substitute the value of the Shop field to the Seller object? Here it is:
@Component public class Shop { }
@Component public class Seller { @Autowired private Shop shop; }
Simply? How much easier! Elegantly? Full Here the following happened: the Component annotation told the spring that the class that she annotated was a bin. Annotation Autowired asked Spring in the field that it annotates to substitute the value. This operation is called “inject”. What kind of value will be substituted? About it a bit later, at first we will understand how in general classes become bins.
We already know that at the beginning of the application, the keeper of all ApplicationContext beans should rise. He creates all the bins at once. Almost all. The fact is that by default, any bin has the intra spring property scope in the value of singleton. Intra since it is not a singleton in the direct sense of the word. It is a singleton for springing: when the context is raised, Spring will create exactly one bean 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 this is not the case now.
So, when raising the ApplicationContext, all the bins are created. Let's find out exactly where the context lives and most importantly: how it determines from which classes you need to create bins. There are several options, for simplicity, we will talk about one of them: configuration using the xml file. Here is his example:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:beans="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="product" class="main.java.Product"></bean> <context:component-scan base-package="main"/> </beans>
This file demonstrates how to start creating bins in two ways. The first, let's say, manual. See, there is a
bean tag with a class indication. This is the bin. From all that is written in this file with the
bean tag, bins will be created.
The second way is less verbose. Remember, above the classes we set the Component annotation. Of all the classes annotated by this annotation, bins will be created. Thanks to this line from the xml file:
<context:component-scan base-package="main"/>
She says to the spring: scan the entire main package and from all that the Component annotation will stand on (or other annotations that are the heirs of the Component), create bins. Compact, isn't it? We just say which packages contain classes from which to create bins, 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 sorted out. How does Spring fill the Shop field when creating the Seller? When the context is raised, a bin class object is created. A bin object of the class Seller is also created, it is also annotated by the Component. It has a Shop type field annotated by Autowired. Annotation Autowired says to the spring: In this field you need to inject the bin. In our case, we have only one bin suitable for this role, that is, the type of which coincides with the field type: this bin is an instance of the class Shop. He will be injected into the object Seller, as required. I understand, now the questions have gotten as worms: what will happen if Spring does not find the necessary bin, or finds some suitable ones (especially considering that you can also inject by interface, and not by class). Spring is smart, but requires the same from us. We need to either have exactly one bin in the system, suitable for every Autowired, or train Spring to deal with such conflicts (we will not talk about this now, you are already tired, bracing, the article is coming to an end).
Note that the Seller is also a bin. If it were not a bin, but created through new, then automatically nothing would be injected into it.
Perhaps you are thinking now why all these difficulties. But imagine that our application is not from 2 classes, but several orders of magnitude more and dependency management is no longer the most trivial task.
Perhaps you are thinking now how beautiful, simple and concisely Spring allows you to inject dependencies. But imagine that something went wrong and you need to debug the application. And things are no longer so simple ...
A couple of hints for last:
- If you have implemented the project and are now at a loss, how can you get the 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 bean, although in modern reality it is not usually done that way. But for a learning example it is possible.
- Since Spring is a framework, you need to connect it to your project. I create an application using maven and add spring-core and spring-context dependencies to the pom.xml file.
Spring IoC contains great opportunities for creating, configuring and injecting beans. We looked at 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.