Spring basics-automated bean assembly and spring basics bean
As mentioned in the previous blog post about Spring's IOC container, although the container is powerful, the container itself is just an empty shell. We need to take the initiative to put the Assembly object and tell it the collaboration relationship between objects, then the container can use its magic as instructed to complete bean assembly's mission. Here, we assemble the act of creating the cooperative relationship between application objects in Spring. Spring provides many bean assembly methods for us to choose during development. We often use three Assembly mechanisms: automatic assembly, Java annotation, and XML configuration. Generally, we call the first one an implicit Assembly mechanism, and the other two are display Assembly mechanisms. In practical applications, the preferred choice for convenience is the implicit automated assembly mechanism. Only when the source code of the bean to be injected is not maintained by your own program, the bean assembly method is considered only when third-party application components are introduced. Of course, all kinds of assembly methods can be freely selected in practical application, and there is no need to stick to either of them during the coding process. It is good to apply. This blog post describes the implicit Assembly mechanism-automated bean assembly.
You must be curious about how Spring implements its automatic assembly mechanism. In fact, Spring is mainly implemented through the following two aspects:
- Component scan-enables Spring to automatically discover beans in the application context by enabling the component scan function;
- Automatic Assembly: automatically satisfies the dependency between components.
Next, let's take a look at how Spring passes throughComponent ScanningAndAutomatic AssemblyTo automatically assemble beans for our applications. Let's first define a car interface:
1 package spring.facade;2 3 public interface Car {4 void drive();5 }
Component Scanning
The main purpose of component scanning is to allow Spring to automatically discover beans in applications through scan control. However, there are so many objects in the program. How does Spring know which objects need to be managed and created? This involves a Spring Component annotation -- @ Component. The annotation class is the Spring Component class. During Spring container loading, beans (PS: in fact, Spring component annotations are classified by semantics as well as @ Controller @ Repository @ Service and so on, respectively acting on the control layer, persistence layer, and business layer. Here is only an Example demonstration, without distinction ). Therefore, we can mark an implementation of the interface with this annotation, indicating that the implementation class is created by Spring --
1 package spring. impl; 2 3 import org. springframework. stereotype. component; 4 import spring. facade. car; 5 6 @ Component 7 public class QQCar implements Car {8 @ Override 9 public void drive () {10 System. out. println ("Driving QQ cars"); 11} 12}
However, Spring annotation scanning is disabled by default, so we also need to display the configuration annotation to start. There are two methods: Java annotation and XML, which are shown respectively --
Java configuration class CarConfig:
package spring.config;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;@Configuration@ComponentScanpublic class CarConfig {}
XML configuration file 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" xmlns: context = "http://www.springframework.org/schema/context" xsi: schemaLocation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-co Ntext. xsd "> <! -- Enable annotation scanning --> <context: component-scan base-package = "spring"/> </beans>
Next, we will write a test class to see if Spring automatically discovers the bean whose annotation is component and creates an object for us --
1 package spring. test; 2 3 import org. junit. test; 4 import org. junit. runner. runWith; 5 import org. springframework. beans. factory. annotation. autowired; 6 import org. springframework. test. context. contextConfiguration; 7 import org. springframework. test. context. junit4.SpringJUnit4ClassRunner; 8 import spring. config. carConfig; 9 import spring. impl. QQCar; 10 import static org. junit. assert. assertNotNull; 11 12/** 13 * annotation meaning: 14 * @ RunWith (SpringJUnit4ClassRunner. class) test running the 15 * @ ContextConfiguration context configuration annotation in the Spring environment, specifying the location of the configuration file (Java class or XML file) 16 */17 @ RunWith (SpringJUnit4ClassRunner. class) 18 // @ ContextConfiguration (classes = CarConfig. class) // method of loading the Java configuration class 19 @ ContextConfiguration (locations = "classpath: resource/applicationContext. xml ") // load the XML configuration in 20 public class CarTest {21 @ Autowired22 private QQCar; 23 24 @ Test25 public void carTest () {26 assertNotNull (car ); 27} 28}
Although the current programming trend is more and more ways to use Java annotations, the test above will show that the test is successful through XML annotations, but the Java annotation method fails, the test will throw the NoSuchBeanDefinitionException, indicating that there is no QQCar component definition, that is, Spring does not find it, Why? The reason is also very simple, that isAnnotation scanning started based on Java annotation can only scan the package where the configuration class is located and its sub-packages by default.If you want to clearly scan components in other packages, You need to specify the annotation in @ ComponetScan, for example, change it to @ ComponentScan ("spring. impl "), the appeal test will pass. If you want to scan multiple packages, configure @ ComponentScan (basePackages = {"spring. impl "," spring. test "}) However, the representation of such strings is of insecure type, and writing the dead packet name is not conducive to code refactoring, we can specify the class or interface contained in the package to specify the package to be scanned, so we can mark it as @ ComponentScan (basePackageClasses = QQCar. class), multiple packages can also be represented in Arrays Using. However, this is still unfriendly to refactoring. The best way is to define an empty mark interface in the package to be scanned. This interface is only used to specify the packet scan range, this minimizes the impact of refactoring.
Automatic Assembly
The previous article only illustrates how to define a class as a Spring component and start Spring component scanning. We have passed the test and confirmed that, spring indeed scans the specified component class and creates an object for us. However, the created object only exists independently and does not produce dependency collaboration with other objects. In actual applications, dependency collaboration between objects is even more common, to scan Spring through components to establish mutual dependency and collaboration for the objects we create based on actual business, we need to use Spring'sAutomatic Assembly. For ease of demonstration, we define another Man class. Man's job is to drive. We first use the constructor injection to satisfy dependencies, check whether Spring will automatically inject the desired Car instance object to us --
package spring.impl;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import spring.facade.Car;@Componentpublic class Man { private Car car; public Man() { } @Autowired public Man(QQCar car) { this.car = car; } public void work() { car.drive(); }}
Test Method --
1 package spring.test; 2 3 import org.junit.Test; 4 import org.junit.runner.RunWith; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.test.context.ContextConfiguration; 7 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 import spring.config.CarConfig; 9 import spring.impl.Man;10 11 @RunWith(SpringJUnit4ClassRunner.class)12 @ContextConfiguration(classes = CarConfig.class)13 public class CarTest {14 15 @Autowired16 Man man;17 18 @Test19 public void carTest() {20 man.work();21 }22 }
As for the above Code, the test is successful. In the test class, Man is scanned by Spring as a component class and an object instance is created. When the instance calls the work method, the instance object that requires Car, And the @ Autowired annotation in the parameter constructor indicates the object dependency. During the program running, spring will automatically inject the Car instance object to satisfy the object dependency, which is the essence of automatic assembly. Actually,Not only can @ Autowired annotation be used on the constructor, but @ Autowired annotation can be used on the Setter method of the attribute or even common methods to satisfy the dependencies between objects.To enable automatic injection --
1 package spring. impl; 2 3 4 import org. springframework. beans. factory. annotation. autowired; 5 import org. springframework. stereotype. component; 6 import spring. facade. car; 7 8 @ Component 9 public class Man {10 private Car car; 11 12 public Man () {13} 14 // constructor for automatic assembly 15 // @ Autowired16 public Man (QQCar car) {17 this. car = car; 18} 19 20 // Setter Method for Automatic Assembly 21 // @ Autowired22 public void setCar (QQCar car) {23 this. car = car; 24} 25 // normal method for automatic assembly 26 @ Autowired27 public void insertCar (QQCar car) {28 this. car = car; 29} 30 31 public void work () {32 car. drive (); 33} 34}
We can still successfully add different methods to the Man class for testing. However, it should be noted that when the non-constructor implements automatic assembly, although we do not have our own new object, the Spring creation instance will use the Man's default constructor, if a parameter constructor is defined in the Man class, the default constructor must be constructed. Otherwise, an exception without the default constructor will be thrown,Remember: you must develop the habit of writing the default constructor in the class to facilitate expansion.
Ambiguity of automatic assembly
If you are careful enough, you will find that the injected Car does not adopt polymorphism in the testing code that meets the automatic assembly requirements, and the code looks very low. In fact, I intentionally injected the specific implementation to pass the test. Of course, the actual business will not write code with such limitations. Because the Boo's main Car interface also has a BenzCar implementation class, if you use a multi-state writing method, automatic assembly will produce ambiguity, and a NoUniqueBeanDefinitionException will be thrown. How can we solve this ambiguity? You must know that every bean managed by Spring container has an ID as a unique identifier. In the above example, we did not explicitly set the ID when describing the components whose QQCar class is Spring,By default, Spring uses the lower-case first letter of the component class name as the bean ID.And we can also customize the ID according to our own business needs --
Package spring. impl; import org. springframework. stereotype. component; import spring. facade. car; // specify chenbenbuyi as the Component ID @ Component ("chenbenbuyi") public class QQCar implements Car {@ Override public void drive () {System. out. println ("drive QQ car ");}}
However, the test found that this does not solve the ambiguity problem of interface parameters during automatic assembly, because the custom ID on the component is a post-release behavior, when you let Spring select the object instance to be automatically injected from multiple interface implementations in the assembly phase, Spring cannot choose-just like you just told me that you want to drive a car, each car also has a unique license plate number, but I still don't know what you want to drive. What should we do? There are a variety of solutions here. We can use the @ Primary annotation to mark the implementation class we need to automatically inject as the bean of choice, just like this --
1 package spring. impl; 2 3 import org. springframework. context. annotation. primary; 4 import org. springframework. stereotype. component; 5 import spring. facade. car; 6 7 @ Component 8 @ Primary 9 public class BenzCar implements Car {10 @ Override11 public void drive () {12 System. out. println ("drive-on"); 13} 14}
During automatic assembly, Spring will give priority to the bean marked as the first choice for automatic injection in the face of ambiguity. Of course, we can also use the qualifier annotation. When @ Autowired is used to complete automatic assembly, only one bean can be used as the automatically injected bean --
1 package spring. impl; 2 3 4 import org. springframework. beans. factory. annotation. autowired; 5 import org. springframework. beans. factory. annotation. qualifier; 6 import org. springframework. stereotype. component; 7 import spring. facade. car; 8 9 @ Component10 public class Man {11 private Car car; 12 13 public Man () {14} 15 // normal Method for Automatic Assembly 16 @ Autowired17 @ Qualifier ("chenbenbuyi") // beans whose ID is chenbenbuyi are assembled in 18 public void insertCar (Car car) {19 this. car = car; 20} 21 22 public void work () {23 car. drive (); 24} 25}
Since then, the automatic assembly of Spring has been almost discussed. The next series of articles will explain the other two Common Assembly mechanisms of Spring-Java annotations and XML configuration. The blog post is original. If you want to repost it, please indicate the source. If you have not stated it properly, please kindly advise. Thank you very much.