Starting from this section, I will learn the most important framework system in the advanced stage of Java, named spring. Spring is the most important part of the entire Java ecosystem. Because I am also a beginner, my generalization is not necessarily comprehensive and refined. Write this chapter just for your future review.
1, Spring IoC container and Bean management
In this section, you will learn about Spring introduction, Spring XML configuration, object instantiation configuration, dependency injection configuration, annotation and Java Config, and Spring implementation unit testing.
1.Spring quick start
IoC control reversal
IoC control inversion, fully known as Inversion of Control, is a design concept.
It is not a technology, but a macro design concept. It is the standard followed by modern programming. The agent creates and manages the object, and the caller obtains the object through the agent. The purpose of IoC is to reduce the direct coupling between objects (or computer code).
The IoC container is added to uniformly manage the objects, so that the object association becomes weak coupling.
Here is a case in life:
I like eating apples very much, but there is a very distressing thing in front of me. There are a wide variety of apples in the market. There are many varieties, tastes and prices of apples. If there are all kinds of apples in front of me now, but I don't know what kind they are and how much they cost. I usually like to eat sweet and crisp apples, so the problem is, if I don't want to get the apples in front of me that best suit my taste without the intervention of others, what should I do? Do I have to turn over a lot of information to understand the types and venues of each fruit, or taste them all? This is a very troublesome thing. This control is initiated by my client. I must master the details of all objects before I can make the right choice. What if there are 100 kinds in front of me? Unless I'm a professional, I'm sure to be hoodwinked.
But fortunately, some smart people see the business opportunities. With the continuous development of market economy, major fruit supermarkets and fruit stores are born. The fruit stall has a boss. When we go to buy fruit, we don't need to know the specific characteristics of these apples. Just find the owner of the fruit stand and say to him, "which apple is crisp and sweet? "Then the boss will tell me" this kind of apple and this kind of apple are crisp and sweet. Their sweetness and taste are different. "Since the fruit stall owner has been engaged in this industry for many years, he certainly knows more than I do. I just need to follow his advice to purchase. It is because of the existence of the fruit stall owner that I handed over the power to obtain the object to the fruit stall owner. He made the corresponding decision for me. The fruit stall owner has greatly facilitated our daily life. Consumers and product objects are coupled through agents. The direct benefit of this decoupling is that objects can change flexibly. If the fruit shop owner finds that there is a more crisp and sweet apple in the market, when we go to buy apples from the boss, it will recommend this better apple to me. I don't care about its other characteristics at all, because it's old The only purpose of the board is to get the best crisp and sweet apples, which is the purpose of IoC's control reversal. There are many examples. For example, when you come to a new city and need to live in peace, you need to find an intermediary instead of casually looking for a landlord. Choosing where to live through the intermediary information solves all kinds of problems in finding a house These are the core embodiment of IoC control inversion in software engineering.
·
DI dependency injection
IoC is a macro design concept, which has nothing to do with programming language. It is the standard followed by modern programming.
DI(Dependency Injection) is a specific technical implementation and a micro implementation. What programming technologies are used in the programming environment to complete the creation and binding of objects during program operation. The specific technologies used by DI in different languages are different.
DI uses reflection technology to implement object injection in Java
·
Spring overview
The meaning of Spring: Spring can be viewed from both narrow and broad perspectives.
Spring in the narrow sense refers to the spring framework, and spring in the broad sense refers to the spring ecosystem.
Spring framework in a narrow sense:
The spring framework is a one-stop solution to the complexity of enterprise development. That is to say, through the spring framework, we can help us make the whole system more complete through the supplement of spring for those parts with unfriendly development experience and missing functions in the actual development. The core of the spring framework is IoC container and AOP aspect oriented programming. IoC container is the basis of all object management, AOP Spring IoC is based on the IoC container. Spring IoC is responsible for creating and managing system objects and expanding functions on this basis.
Spring ecosystem in a broad sense:
Spring IoC is the core and basic part of the whole spring ecosystem. This is because all objects in the system are managed as a whole. On this basis, it has taken root and expanded several sub projects with different functions. It can be said that spring is a one-stop comprehensive solution, whether it is web application development, Android development or distributed development All applications are OK.
The above is what Spring can do. Based on these things, dozens of different projects have been derived, including Spring Boot, Spring Framework, spring data, spring cloud, spring cloud data flow, spring security, spring session, Spring graphql, etc. for details, please refer to https://spring.io/projects
At this stage, I want to learn the Spring Framework.
Traditional development method: object direct reference leads to hard Association of objects, and the program is difficult to expand and maintain.
If A user wants to use the object A, but the object A implements A function, it needs to create an additional object B. You can regard object A as A service and object B as A Dao. This process is our previous standard development method. But it also has A huge disadvantage. When this Dao evolves continuously with our development process, it is no longer suitable. Other programmers develop A C object. If A wants to abandon B and choose C, it needs to modify the source code and go to the new C object. This means that our program needs to be compiled and re launched before it can take effect. It will involve re testing, re distribution, re approval, etc. This set of processes is very cumbersome. Therefore, in the process of our project practice, it is not recommended that specific users create corresponding objects in new.
Then we need to use the Spring IoC container to create and manage these objects in a passive form, and our users just extract the objects in the container. IoC is the low base of Spring ecology, which is used to uniformly create and manage object dependencies.
Take the example just now. If it is replaced by Spring, first of all, as Spring, it will provide A Spring IoC container. This container is actually an abstract thing, which is equivalent to opening up A space in our Java running memory, which is managed by Spring. All objects are not created by our users or A objects, but are created by the Spring IoC container. After creation, the A object depends on the B object. Of course, it will not use the A object to create A new B object, but inject the dependent B object of the A object into the A object through reflection technology. As A user, I don't need to focus on how many objects are inside the container and what the relationship between objects is. I just need to focus on where to extract the objects I need. In other words, it is no longer oriented to specific objects, but to the container, and the required objects are obtained through the container.
Take the fruit stall as an example. spring IoC is the fruit warehouse. The spring framework is the owner of the fruit stall, and the user is the customer. The customer tells the owner of the fruit stall to give me red fruit on a big day, and the boss extracts the fruit that meets the requirements.
Responsibilities of Spring IoC container:
Give the control of the object to the third-party control management (IoC control inversion), use java reflection technology to realize runtime object creation and Association (DI dependency injection), and improve the maintainability and expansibility of the application based on configuration.
·
Spring IoC first experience
The cases are as follows:
According to the different tastes of children, how can we let children get their favorite apples directly?
The following is a demonstration through traditional procedures:
Open Idea and create an empty Maven project named s01
Create two java entity classes in entity: Apple and Child. The code is as follows:
Apple.java
package com.haiexijun.ioc.entity; public class Apple { private String title; private String color; private String origin; public Apple() { } public Apple(String title, String color, String origin) { this.title = title; this.color = color; this.origin = origin; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public String getOrigin() { return origin; } public void setOrigin(String origin) { this.origin = origin; } }
Child.java
package com.haiexijun.ioc.entity; public class Child { private String name; private Apple apple; public Child() { } public Child(String name, Apple apple) { this.name = name; this.apple = apple; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Apple getApple() { return apple; } public void setApple(Apple apple) { this.apple = apple; } public void eat(){ System.out.println(name+"Yes"+apple.getOrigin()+"Planted"+apple.getTitle()); } } ```java Then create a Application To write logical code: package com.haiexijun.ioc; import com.haiexijun.ioc.entity.Apple; import com.haiexijun.ioc.entity.Child; public class Application { public static void main(String[] args) { //Create 3 apples Apple apple1=new Apple("Red Fuji","gules","Europe"); Apple apple2=new Apple("green apple","green","Central Asia"); Apple apple3=new Apple("Jin Shuai","yellow","China"); //Create a child and complete the association between the apple and the child Child lily=new Child("Lily",apple1); Child andy=new Child("Andy",apple2); Child luna=new Child("Luna",apple3); //Children eat apples lily.eat(); andy.eat(); luna.eat(); } }
After running, we can get the results we need.
However, the above codes will have many disadvantages in practical work. Let's analyze them according to the following:
First of all, these descriptions of apple are written in the program code, and we know that these attributes of Apple will change accordingly with season and time. Once the properties change, we must modify the source code of the program and republish and re launch the application. And if I write three objects in my program, it will only create three objects. If you add a new child, you have to modify the source code, and the maintainability and scalability of the program are very insufficient. Moreover, it is hard correlation. It creates objects and sets them by constructing method parameters, which means that after our program runs, the relationship between the child and apple has been determined. This determination relationship is completed when the program is compiled, which is a very rigid thing. For example, now that Luna has grown up, she no longer likes this soft Jin Shuai. He also begins to like red Fuji. Moreover, lily also wants to taste Jin Shuai. If she writes according to the above traditional method, she has to modify the source code for adjustment.
The next section uses Spring IoC to implement this case.
Using XML to implement Spring IoC
Let's modify the above code into a Spring IoC management program to experience the power of Spring IoC.
Before that, I must emphasize that this is our first contact with spring. The purpose of this case is to give us a perceptual understanding of spring. We will learn more about each configuration and attribute in the following sections.
1. Configure Maven dependency
Spring context is the smallest dependency range of spring IoC container. Only when spring context is introduced can we manage the program.
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.13</version> </dependency>
After importing dependencies, click External Libraries to see which dependencies have been imported:
2. Create an XML file in the resources directory, applicationContext.xml
applicationContext.xml is the core configuration file of spring IoC. All object creation and associated settings are carried out in this XML.
So how to configure this xml? We first open the spring official website, and then enter the Core part of the Spring Framework help document in Project. Then find the xml configuration of 1.2.1 and copy its schema constraint into the configuration file.
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-metadata
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>
Then the idea is also very intelligent and will immediately display whether to create a context.
We click create, and then click OK to confirm
Write bean
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!--stay IoC When the container is started, it is automatically controlled by Spring instantiation Apple,Named sweetApple Put into container--> <bean id="sweetApple" class="com.haiexijun.ioc.entity.Apple"> <property name="title" value="Red Fuji"></property> <property name="origin" value="Europe"></property> <property name="color" value="gules"></property> </bean> <bean id="sourApple" class="com.haiexijun.ioc.entity.Apple"> <property name="title" value="green apple"></property> <property name="origin" value="Central Asia"></property> <property name="color" value="green"></property> </bean> <bean id="softApple" class="com.haiexijun.ioc.entity.Apple"> <property name="title" value="Jin Shuai"></property> <property name="origin" value="China"></property> <property name="color" value="yellow"></property> </bean> </beans>
3. Create a java class with main method and write the following code:
package com.haiexijun.ioc; import com.haiexijun.ioc.entity.Apple; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringApplication { public static void main(String[] args) { //Load the specified xml file to initialize the IoC container. context itself refers to the spring IoC container ApplicationContext context=new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); Apple sweetApple = context.getBean("sweetApple", Apple.class); System.out.println(sweetApple.getTitle()); } }
Print Fuji after running.
When we compare, we will find a great advantage, that is, the original code is changed into configurable text, and the configuration can be changed in xml text. The content can be adjusted without moving any line of source code.
Let's continue to write code to demonstrate object association.
4. Add three child object bean s in applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!--stay IoC When the container is started, it is automatically controlled by Spring instantiation Apple,Named sweetApple Put into container--> <bean id="sweetApple" class="com.haiexijun.ioc.entity.Apple"> <property name="title" value="Red Fuji"/> <property name="origin" value="Europe"/> <property name="color" value="gules"/> </bean> <bean id="sourApple" class="com.haiexijun.ioc.entity.Apple"> <property name="title" value="green apple"/> <property name="origin" value="Central Asia"/> <property name="color" value="green"/> </bean> <bean id="softApple" class="com.haiexijun.ioc.entity.Apple"> <property name="title" value="Jin Shuai"/> <property name="origin" value="China"/> <property name="color" value="yellow"/> </bean> <bean id="lily" class="com.haiexijun.ioc.entity.Child"> <property name="name" value="Lily"/> <property name="apple" ref="sweetApple"/> </bean> <bean id="andy" class="com.haiexijun.ioc.entity.Child"> <property name="name" value="Andy"/> <property name="apple" ref="sourApple"/> </bean> <bean id="luna" class="com.haiexijun.ioc.entity.Child"> <property name="name" value="Luna"/> <property name="apple" ref="softApple"/> </bean> </beans>
The Child inside is associated with the apple through the ref attribute.
Write the test code below
package com.haiexijun.ioc; import com.haiexijun.ioc.entity.Apple; import com.haiexijun.ioc.entity.Child; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringApplication { public static void main(String[] args) { //Load the specified xml file to initialize the IoC container. context itself refers to the spring IoC container ApplicationContext context=new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); Apple sweetApple = context.getBean("sweetApple", Apple.class); System.out.println(sweetApple.getTitle()); //Extract the object with beanId=lily from the IoC container Child lily=context.getBean("lily",Child.class); lily.eat(); Child andy=context.getBean("andy",Child.class); andy.eat(); Child luna=context.getBean("andy",Child.class); luna.eat(); } }
After running, they all ate fruit.
Maybe you haven't fully found the benefits of IoC here. However, through the configuration of xml, the maintainability and expansibility of the program can be improved in future projects.
Here, I have a basic understanding of spring IoC. The configurations will be explained in detail below.
·
2.XML management object (Bean)
When we manage Spring, when we talk about objects, we are all JavaBeans. JavaBean is not a special technology, it is some coding requirements for reusable objects in Java. Such as property private, constructor, getter and setter methods, etc. In the Spring IoC container, JavaBeans are managed one by one. So I'll use Bean to refer to the objects in the container later.
spring's Bean management includes three configuration methods.
The first is to configure beans based on XML, the second is to configure beans based on annotations, and the third is to configure beans based on Java code.
The essence of these three ways of configuring beans is the same. They all tell the Spring IoC container how to instantiate and manage these beans. It's just that their forms are different.
Next, let's explain how to manage objects based on XML. Let's go back to the example above.
The file name applicationContext.xml is commonly called by convention, which is generally called this name. In the configuration file, all objects use the label Bean uniformly. The id attribute inside refers to the (unique identification) name of the object after the specified class is instantiated by the IoC container. Here we call it sweetApple. Class specifies the class to be instantiated by the IoC container. As for < property > in the < Bean > tag, it is not necessary. We will explain in detail later when we explain attribute injection.
spring provides ApplicationContext to create IoC containers. ApplicationContext is an interface with many specific implementation classes. The ClassPathXmlApplicationContext class implements the process of reading the specified xml file from the current class path and parsing and loading. ClassPathXmlApplicationContext is one of the most commonly used core classes. When this code is run, it means that the IoC container is saved in our Java running memory, and the IoC container creates corresponding objects and manages these objects according to the configuration just now.
ClassPathXmlApplicationContext allows you to load multiple xml files in the form of an array of incoming strings.
String[] configLocations=new String[]{"classpath:applicationContext.xml","classpath:applicationContext-1.xml"}; ApplicationContext context=new ClassPathXmlApplicationContext(configLocations);
3. Three configuration methods of XML instantiated beans:
1. Instantiate the object based on the default parameterless construction method
2. Instantiate objects based on static factories
3. Instantiate the object based on the factory instance method
Instantiate an object based on the default parameterless construction method
Create an empty Maven project named s02 and introduce the dependency spring context. As before, create the entity package, which creates Apple and Child classes.
Then write the applicationContext.xml configuration file.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!--bean Labels create objects through the default parameterless constructor of the class--> <bean id="apple1" class="com.haiexijun.entity.Apple"> </bean> </beans>
An Apple object named apple1 is configured inside.
Then add a line of code in the parameterless default constructor of Apple class:
public Apple() { System.out.println("Apple Object created,"+this); }
Write the following code in the java class with main method and run it. You will find Apple's default parameterless constructor
package com.haiexijun.app; import com.haiexijun.entity.Apple; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringApplication { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); } }
Operation results:
If you want to use the construction method with parameters to create objects, what to do will be explained in detail in the next section.
·
Instantiating objects using parameter construction method
Insert a < constructor Arg > sub tag under the bean. In this sub tag, we can set the name attribute to specify the parameters of the construction method with parameters, and set the corresponding value with the value attribute.
In addition to using the name attribute to specify the parameters of the construction method, you can also use the index attribute to set the specified parameters and the value attribute to set the value. Index indicates the front and back position of the parameter. Index starts at 0.
In practice, the first method is recommended to transmit parameters. There will be no demonstration here. However, when there are multiple parameter constructors, for example, a class has a construction method with 3 parameters and a construction method with 4 parameters. When we pass in several parameters in the bean, the program calls the construction method of the corresponding number of parameters.
·
Instantiate objects based on static factories
In fact, this way of instantiating objects is not used much. It is mainly to construct methods to instantiate objects. What we usually say about the factory actually refers to the factory model. As a factory pattern, its fundamental purpose is to hide the details of creating classes. Create the objects we need through an additional factory class. According to the form of factory, it can be divided into static factory and factory instance.
We use code cases to understand and experience instantiated objects based on static factories.
Create a factory sub package to save the factory class. Then create a java class AppleStaticFactory. There is a method for creating sweet apples.
package com.haiexijun.factory; import com.haiexijun.entity.Apple; public class AppleStaticFactory { public static Apple createSweetApple(){ Apple apple=new Apple(); apple.setTitle("Red Fuji"); apple.setOrigin("Europe"); apple.setColor("gules"); return apple; } }
This factory is then invoked in applicationContext.xml to create objects.
<!--Get objects using static factories--> <bean id="apple4" class="com.haiexijun.factory.AppleStaticFactory" factory-method="createSweetApple"/>
Here you may ask, is this method of creating objects the same as the traditional new method before? In fact, it is very different. The responsibility of the factory is to create objects through static methods and hide the details of creating objects. How to create objects is invisible to us. The creator only needs to know, but after calling the createSweetApple of the factory class, a sweet apple will be created.
·
Instantiate objects based on factory instance methods
Factory instance method creation object refers to the process that IoC container instantiates the factory class and calls the corresponding factory instance method to create an object.
Create a java class AppleFactoryInstance under the factory package, and the method in it has no static keyword.
package com.haiexijun.factory; import com.haiexijun.entity.Apple; /** * Factory instance method creation object refers to the process that IoC container instantiates the factory class and calls the corresponding factory instance method to create an object. */ public class AppleFactoryInstance { public Apple createSweetApple(){ Apple apple=new Apple(); apple.setTitle("Red Fuji"); apple.setOrigin("Europe"); apple.setColor("gules"); return apple; } }
Call the factory to create objects in applicationContext.xml, and create 2 bean here.
<!--Get objects using factory instance method--> <bean id="factoryInstance" class="com.haiexijun.factory.AppleFactoryInstance"/> <bean id="apple5" factory-bean="factoryInstance" factory-method="createSweetApple"/>
It is said that as spring becomes more and more powerful, it is less and less to create objects based on factories in the actual use environment. Let's just have a general understanding.
·
4. Get Bean from IoC container
·
The bean of the xml file has id and name attributes.
The similarities between id and name attributes are:
The id and name in the bean are the unique identification of the setting object in the IoC container. It's essentially the same. Both are not allowed to duplicate in the same configuration file. Both allow duplication in multiple configuration files, and the new object overwrites the old object.
But they also have many differences: id requirements are more stringent, and only one object id can be defined at a time. name is more relaxed, allowing multiple object IDs to be defined at a time. We recommend using id.
Note: the naming requirements of id and name should be meaningful and written according to the hump name.
Understand: without setting id and name
<bean class="com.haiexijun.entity.Apple"> <constructor-arg index="0" value="Red Fuji"/> <constructor-arg index="1" value="gules"/> <constructor-arg index="2" value="Europe"/> <constructor-arg index="3" value="12.1"/> </bean>
Use the full class name as the identification of the bean
Apple apple3 = context.getBean("com.haiexijun.entity.Apple",Apple.class);
·
5. Path expression
As a parameter of ClassPathXmlApplicationContext, an expression is required to be passed in. The meaning of this expression is to load the xml file named applicationContext under our current classpath.
The resources in the source code directory is not our class path, but the real class path is in the classes directory under the target directory generated by the project.
·
ClassPathXmlApplicationContext can load multiple configuration files, create a String array, and load xml configuration files in order.
6. Object dependency injection
In this section, let's learn how to set the dependencies of objects in the spring IoC container. This process is called dependency injection. Dependency injection is simply to associate two objects.
Dependency injection refers to the operation of assigning objects in the container to other objects by reflection at run time.
Static value injection using setter
The properties contained in this bean are all referred to by the tag property. There are two basic properties in the property tag. The first is name, which refers to the property name, and the second is value, which refers to the property value. After the bean is created, the setter method of each property will be called through the reflection mechanism to dynamically set the value.
·
Injecting objects based on construction method
·
Injection collection object
The previously written injection objects are all single objects. In this section, I will explain collection object injection such as List or Set. Note the case of each label.
You can use the value tag to set specific values, or ref to introduce specific bean s.
The difference between list and set is that the elements in list are not allowed to be repeated, while the elements in set can be repeated.
properties only allow key and value to be string types.
Here is an example:
<bean id="company" class="com.haiexijun.ioc.entity.Company"> <property name="rooms"> <list> <value>201-President's Office</value> <value>202-General Manager's Office</value> <value>203-Conference room of R & D department</value> <value>203-Conference room of R & D department</value> </list> </property> <property name="computers"> <map> <entry key="dev-112" value-ref="computer1"></entry> <entry key="dev-113"> <bean class="com.haiexijun.ioc.entity.Computer"> <constructor-arg name="brand" value="association"/> <constructor-arg name="price" value="2222"/> <constructor-arg name="sn" value="12312412"/> <constructor-arg name="type" value="Desktop"/> </bean> </entry> </map> </property> <property name="info"> <props> <prop key="phone">13214124</prop> <prop key="address">zjian</prop> </props> </property> </bean> <bean id="computer1" class="com.haiexijun.ioc.entity.Computer"> <constructor-arg name="brand" value="association"/> <constructor-arg name="price" value="2222"/> <constructor-arg name="sn" value="12312412"/> <constructor-arg name="type" value="Desktop"/> </bean>
I won't explain too much here.
·
View objects in container
Earlier, you learned how to create objects in a container and design the relationships of objects. But all this information is done through brain compensation. If a project gets bigger and bigger,
There are more and more objects. How do we know how many objects are in the current container and what types these objects are?
You can get the String array of bean names through the getBeanDefinitionNames() method of the ApplicationContext object. Get the full class name through the class object. getClass().getName(), such as context.getBean(beanName).getClass().getName(). If you want to get the specific content of the entity class, use the entity class object. toString() method.
·
·
7. Scope and life cycle of bean object
·
Detailed explanation of bean scope attribute
Scope originally means scope.
The bean scope property is used to determine when an object is created and the scope of action. Setting the scope property will affect the number of objects in the container. By default, beans will be instantiated automatically after the IoC container is created, which is globally unique.
The following is a single example of singleton
The execution efficiency of a singleton is high. It does not need to create the same object multiple times, but it will also cause many thread safety problems.
The following is a schematic diagram of several examples of prototype:
·
·
Comparison between singleton and prototype:
·
IoC life cycle
·
·
Here is a case to demonstrate the life cycle of IoC:
After configuring the project, create an entity class named Order:
package com.haiexijun.ioc.entity; public class Order { private Float price; private Integer quantity; private Float total; public Order(){ System.out.println("establish Order object"+this); } public void init(){ System.out.println("implement Init method"); total=price*quantity; } public void destroy(){ System.out.println("Destroy container, release Order Object related resources"); } public void pay(){ System.out.println("The order amount is:"+total); } public Float getPrice() { return price; } public void setPrice(Float price) { System.out.println("set up price: "+price); this.price = price; } public Integer getQuantity() { return quantity; } public void setQuantity(Integer quantity) { System.out.println("set up quantity: "+quantity); this.quantity = quantity; } public Float getTotal() { return total; } public void setTotal(Float total) { this.total = total; } }
Then write the bean in the applicationContext.xml file
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="order1" class="com.haiexijun.ioc.entity.Order" init-method="init" destroy-method="destroy"> <property name="price" value="19.8"/> <property name="quantity" value="1000"/> </bean> </beans>
You will find that instead of setting the attribute and attribute value for the total attribute in the bean, I set the total attribute by specifying the init method in the order class through the init method attribute to calculate the total. We also define a destroy method attribute to specify the method to be executed after the IoC container is destroyed, which is generally used to release the resources associated with the object.
Finally, write code to run:
package com.haiexijun.ioc; import com.haiexijun.ioc.entity.Order; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringApplication { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); System.out.println("====IoC Container initialization completed===="); Order order= context.getBean("order1", Order.class); order.pay(); //When the container is destroyed, the destroy method set in xml will be called automatically ((ClassPathXmlApplicationContext)context).registerShutdownHook(); } }
The operation results are as follows:
·
·
8. Configure IoC container based on annotation and Java Config
Annotation based advantages:
Get rid of the cumbersome XML form of bean s and dependency injection configuration. Based on the "declarative" principle, it is more suitable for lightweight modern enterprise applications. It makes the code more readable and the R & D personnel have a better development experience.
Spring three types of annotations:
Component type annotation: declares the functions and responsibilities of the current class.
Auto assembly annotation: automatically inject objects according to attribute characteristics
Metadata annotation: more detailed auxiliary IoC container management object
Component type annotation
Annotation of four component types
annotation | explain |
---|---|
@Component | Component annotation, general annotation, and the class described by this annotation will be managed and instantiated by the IoC container |
@Controller | Semantic annotation, indicating that the current class is the controller class in MVC application |
@Service | Semantic annotation, indicating that the current class is a Service business logic class |
@Repository | Semantic annotation, indicating that the current class is used in the business persistence layer, usually describing the corresponding Dao class |
If these annotations are to be recognized by Spring, you should also configure to enable component scanning:
<context:component-scan base-package="Package name of the project"> <!--Optional, excluding packages that do not want to be scanned--> <context:exclude-filter type="regex" expression="Package name"/> </context:component-scan>
The following is a case to demonstrate the Component annotation:
After creating a project and configuring the dependencies, create the applicationContext.xml file in the resources directory. You may wonder why you explicitly use annotations and what to do with this XML file. This is because we still need to write some of the most basic configurations in this XML configuration file. Moreover, the XML constraints based on annotations are different from the previous XML constraints To copy the official website document 1.9 https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-annotation-config The schema configuration of the annotation.
<?xml version="1.0" encoding="UTF-8"?> <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" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="Package name for scanning annotations"/> </beans>
The following is a case:
<?xml version="1.0" encoding="UTF-8"?> <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" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.haiexijun"/> </beans>
We specify to scan the spring annotation under the com.haiexijun package.
Then, create a UserDao class under the dao package, and we use the @ Repository annotation on it.
package com.haiexijun.ioc.dao; import org.springframework.stereotype.Repository; //The default beanId of component type annotation is lowercase (for example, the beanId of UserDao below is UserDao by default) //You can also pass in a custom beanid to @ Repository() (for example, @ Repository("udao")) @Repository public class UserDao { public UserDao(){ } }
Just write @ Repository in front of the class name. Note that the default beanId of the component type annotation is lowercase (for example, the beanId of UserDao below is UserDao by default)
Let's write the main method to demonstrate:
package com.haiexijun.ioc; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringApplication { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); String[] ids=context.getBeanDefinitionNames(); for (String id:ids){ System.out.println(id+":"+context.getBean(id)); } } }
This main method prints all bean objects created in the IoC container. The running results are as follows:
Note that there is no beanId set for UserDao, so spring will make his beanId lowercase by default.
We can use @ Repository("udao") to set our custom beanId for it
The operation results are as follows:
If you want to annotate the Service business logic class, use the @ Service annotation
package com.haiexijun.ioc.service; import org.springframework.stereotype.Service; @Service public class UserService { }
If you want to annotate the controller, annotate it with @ controller
package com.haiexijun.ioc.controller; import org.springframework.stereotype.Controller; @Controller public class UserController { }
If you want to annotate a tool class, spring does not annotate the tool class, so you can use @ Component annotation
package com.haiexijun.ioc.utils; import org.springframework.stereotype.Component; @Component public class StringUtils { }
You can set a custom beanId for all annotations. Moreover, these beans are singleton in the container.
Auto assembly annotation
There are two kinds of automatic assembly annotations. The purpose of automatic assembly annotation is to automatically inject data for an attribute during the operation of IoC container, which exists for dependency injection.
Assembly by type is not recommended in the industry, and assembly by name is more encouraged@ Named should be used with the previous @ Inject@ The resource annotation will first Inject dependency by name, but when the name is not satisfied, it will Inject dependency by type@ Resource is the most powerful automatic assembly annotation.
Here's the code demonstration:
Let's go back to the above case. After learning MVC, we all know that MVC is called layer by layer in a hierarchical manner. That is, the controller depends on the service, and the service depends on the dao.
Let's take a brief look at @ Autowired
package com.haiexijun.ioc.service; import com.haiexijun.ioc.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserDao userDao; }
@The Autowired annotation is used on the attribute to be injected. Note that this annotation injects attributes by type. If two classes are implemented on the UserDao interface, it will report an error. The solution is to remove the @ Repository annotation of the implementation class, or add a @ primary annotation after the @ Repository annotation of the class to be injected, and @ Autowired will give priority to injecting the class with @ primary annotation.
The following is a demonstration of the basic use of @ Resource annotation:
If the @ Resource annotation sets the name attribute, it is injected into the IoC container by name. If the @ Resource annotation does not set the name attribute, the bean will be matched in the IoC container with the attribute name as the bean name. If there is a match, it will be injected. If there is no match by attribute name, match by type. Like @ Autowired, you need to add @ Primary annotation to solve the type conflict. Usage suggestion: when using @ Resource, it is recommended to set name or ensure that the property name is consistent with the bean name.
package com.haiexijun.ioc.service; import com.haiexijun.ioc.dao.UserDao; import javax.annotation.Resource; import org.springframework.stereotype.Service; @Service public class DepService { @Resource private UserDao userDao; }
Either @ Autowired or @ Resource can complete object injection based on not using setter method. Their essence is to change the attribute to be injected from private to public by using the reflection mechanism at runtime, and then complete the direct assignment of the attribute. After the assignment, change it back to private.
Metadata annotation
Its function is to provide some auxiliary information for the Spring IoC container when managing objects.
@Scope and scope in the previous xml are the same usage, setting single instance and multiple instances. singleton and prototype.
It's not convenient to demonstrate here. We'll review it later in the project.
9. Configure Spring IoC container based on Java Config
Java Config is a new configuration method introduced after spring 3.0. Its main principle is to use java code to replace traditional XML files.
Advantages based on Java Config: completely free from the constraints of XML and use independent Java classes to manage objects and dependencies. Annotations are relatively scattered, and configuration can be centrally managed by using Java Config. Dependency checking can be performed at compile time, which is not error prone. The annotation configuration based on Java Config has better development experience, while the configuration based on XML has better maintainability. We choose different configuration schemes according to different situations in the actual project.
Java Config core annotation
Recreate a project to demonstrate basic usage:
Create dao, service and controller packages respectively, and then create corresponding java classes, as follows:
UserDao.java
package com.haiexijun.dao; public class UserDao { }
UserService.java
package com.haiexijun.service; import com.haiexijun.dao.UserDao; public class UserService { private UserDao userDao; //To use Java Config, keep getter s and setter s public UserDao getUserDao() { return userDao; } public void setUserDao(UserDao userDao) { this.userDao = userDao; } }
UserController.java
package com.haiexijun.controller; import com.haiexijun.service.UserService; public class UserController { private UserService userService; public UserService getUserService() { return userService; } public void setUserService(UserService userService) { this.userService = userService; } }
To use Java Config, keep its getter and setter methods.
Then just create another Config class to configure instead of xml:
Use @ configuration annotation to identify that this is a Config configuration class. The beans to be managed are marked with @ bean annotation. Java Config uses methods to create objects, and puts the method return objects into the container. beanId is the method name.
package com.haiexijun; import com.haiexijun.controller.UserController; import com.haiexijun.dao.UserDao; import com.haiexijun.service.UserService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; //The current class is a configuration class that replaces applicationContext.xml @Configuration public class Config { //Java Config uses methods to create objects, and puts the returned object of the method into the container. beanId is the method name @Bean public UserDao userDao(){ UserDao userDao=new UserDao(); return userDao; } @Bean public UserService userService(){ UserService userService=new UserService(); return userService; } @Bean public UserController userController(){ UserController userController=new UserController(); return userController; } }
Then write a code demonstration in the main method:
package com.haiexijun; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class SpringApplication { public static void main(String[] args) { //The IoC container is initialized here by creating an AnnotationConfigApplicationContext object ApplicationContext context=new AnnotationConfigApplicationContext(Config.class); String[] ids= context.getBeanDefinitionNames(); for (String id:ids){ System.out.println(id+":"+context.getBean(id)); } } }
Instead of using ClassPathXmlApplicationContext to create IoC containers, we use AnnotationConfigApplicationContext to create IoC containers. Then I facilitated the information about beans managed by IoC container. The running results are as follows:
The above only describes how to configure IoC container to manage beans. Let's learn how to configure object dependency injection.
Change the code of the Config class above:
package com.haiexijun; import com.haiexijun.controller.UserController; import com.haiexijun.dao.UserDao; import com.haiexijun.service.UserService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; //The current class is a configuration class that replaces applicationContext.xml @Configuration public class Config { //Java Config uses methods to create objects, and puts the returned object of the method into the container. beanId is the method name @Bean public UserDao userDao(){ UserDao userDao=new UserDao(); System.out.println("Created userDao"); return userDao; } @Bean //Injection will be attempted by name. If name does not exist, injection will be attempted by type public UserService userService(UserDao userDao){ UserService userService=new UserService(); System.out.println("Created userServices"); userService.setUserDao(userDao); System.out.println("call setUserDao"+userDao); return userService; } @Bean public UserController userController(UserService userService){ UserController userController=new UserController(); System.out.println("Created userController"); userController.setUserService(userService); System.out.println("call setUserService"+userService); return userController; } }
To implement dependency injection, you only need to pass in the object to be injected in the @ Bean method. It attempts to inject by name, or by type if name does not exist.
Then write the main method test:
package com.haiexijun; import com.haiexijun.controller.UserController; import com.haiexijun.dao.UserDao; import com.haiexijun.service.UserService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; //The current class is a configuration class that replaces applicationContext.xml @Configuration public class Config { //Java Config uses methods to create objects, and puts the returned object of the method into the container. beanId is the method name @Bean public UserDao userDao(){ UserDao userDao=new UserDao(); System.out.println("Created userDao"); return userDao; } @Bean //Injection will be attempted by name. If name does not exist, injection will be attempted by type public UserService userService(UserDao userDao){ UserService userService=new UserService(); System.out.println("Created userServices"); userService.setUserDao(userDao); System.out.println("call setUserDao"+userDao); return userService; } @Bean public UserController userController(UserService userService){ UserController userController=new UserController(); System.out.println("Created userController"); userController.setUserService(userService); System.out.println("call setUserService"+userService); return userController; } }
The operation results are as follows:
Of course, as mentioned above, spring will try to inject by name first. If name does not exist, it will inject by type. Let's try to see how to run when name does not exist:
For example, we changed some codes of the Config class. We changed the name of the parameter of the object to be injected from userDao to udao:
@Bean //Injection will be attempted by name. If name does not exist, injection will be attempted by type public UserService userService(UserDao udao){ UserService userService=new UserService(); System.out.println("Created userServices"); userService.setUserDao(userDao); System.out.println("call setUserDao"+userDao); return userService; }
Then run, in fact, there will be no error. Because of spring, he will find bean s of the same type in the container for injection.
However, if the name of the bean to be injected is incorrect and there are two beans with the same injection type in the container, an error will be reported. For example, we will create two UserDao beans below.
@Bean public UserDao userDao(){ UserDao userDao=new UserDao(); System.out.println("Created userDao"); return userDao; } @Bean public UserDao userDao1(){ UserDao userDao=new UserDao(); System.out.println("Created userDao"); return userDao; } @Bean //Injection will be attempted by name. If name does not exist, injection will be attempted by type public UserService userService(UserDao udao){ UserService userService=new UserService(); System.out.println("Created userServices"); userService.setUserDao(udao); System.out.println("call setUserDao"+udao); return userService; }
At this time, running the main method will report an error.
It shows that two beans of the same type are found, and it does not know which bean to inject.
There are many ways to solve this problem, such as adding an @ Primary annotation to one of the bean s. Make it priority to be injected.
@Bean public UserDao userDao(){ UserDao userDao=new UserDao(); System.out.println("Created userDao"); return userDao; } @Bean @Primary public UserDao userDao1(){ UserDao userDao=new UserDao(); System.out.println("Created userDao"); return userDao; } @Bean //Injection will be attempted by name. If name does not exist, injection will be attempted by type public UserService userService(UserDao udao){ UserService userService=new UserService(); System.out.println("Created userServices"); userService.setUserDao(udao); System.out.println("call setUserDao"+udao); return userService; }
The operation results are as follows, and no error will be reported at this time:
The following describes how to configure bean s with @ Scope annotation. The Scope is singleton or prototype
@Bean @Scope("prototype") public UserController userController(UserService userService){ UserController userController=new UserController(); System.out.println("Created userController"); userController.setUserService(userService); System.out.println("call setUserService"+userService); return userController; }
In fact, Java Config can be used together with annotation configuration. You only need to configure @ ComponentScan annotation, which represents the annotation of the components in the scanning package. basePackages are used to specify the package to scan the annotation.
@Configuration @ComponentScan(basePackages = "com.haiexijun") public class Config { //Java Config uses methods to create objects, and puts the returned object of the method into the container. beanId is the method name @Bean public UserDao userDao(){ ``````````````` }
10. Integration of spring and JUnit4 - Spring Test test module
Spring Test is a module used for testing in spring. Spring Test has good integration with JUnit unit testing framework. In daily development, one of the most commonly used functions is to integrate with JUnit unit test. Through Spring Test, the IoC container can be automatically initialized during JUnit unit test.
Integration process of Spring and JUnit 4
1. Configure Maven project to rely on spring test and junit4
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.3.13</version> </dependency>
2. Use @ RunWith and @ ContextConfiguration to describe the test case class. With @ RunWith, spring can take over the control of JUnit4 and complete the initialization of IoC, @ ContextConfiguration is used to specify which configuration file to load during the initialization of IoC container.
3. The test case class obtains the object from the container to complete the execution of the test case.
Let's demonstrate:
After creating the userDao class and UserService class, configure the xml file
package com.haiexijun.ioc.dao; public class UserDao { public void insert(){ System.out.println("Insert a piece of data"); } }
package com.haiexijun.ioc.service; import com.haiexijun.ioc.dao.UserDao; public class UserService { private UserDao userDao; public void createUser(){ System.out.println("Call the business code to create the user"); userDao.insert(); } public UserDao getUserDao() { return userDao; } public void setUserDao(UserDao userDao) { this.userDao = userDao; } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userDao" class="com.haiexijun.ioc.dao.UserDao"> </bean> <bean id="userService" class="com.haiexijun.ioc.service.UserService"> <property name="userDao" ref="userDao"/> </bean> </beans>
Then create a test case class springtester under the test package:
import com.haiexijun.ioc.service.UserService; import org.junit.Test; import org.junit.runner.RunWith; import javax.annotation.*; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; //Hand over the execution authority of Junit4 to Spring Test, and then automatically initialize the IoC container before the execution of the test case @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:applicationContext.xml"}) public class SpringTestor { @Resource private UserService userService; @Test public void testUserService(){ userService.createUser(); } }