Write in front
In the previous articles, we said that the Spring container supports three ways to configure bean definition information. Now we will explain it in detail:
- XML: bean definitions and dependencies are configured in XML files, which is complicated.
- Annotation based: inject dependencies directly, configure the scanning package path in xml file, and simplify xml a lot.
- Java based: batch scan and register bean s through configuration classes and annotations, and xml files are no longer required.
The previous cases are based on XML. This article introduces the annotation based method.
Content of this article
- Getting started with a simple case: using annotation based container configuration
- Detailed use of Autowired, in conjunction with @ Primary and @ Qulifier
Introduction to case 1
The bean definition information of the entry case is in the xml file, and annotations are used for dependency injection.
@Autowired: mark the constructor, field, setting method or configuration method as automatically assembled by Spring's dependency injection tool.
Define classes and inject dependencies through @ Autowird
public class BeanOne { } public class BeanTwo { // Annotation injection BeanOne @Autowired private BeanOne beanOne; @Override public String toString() { return "BeanTwo{" + "beanOne=" + beanOne + '}'; } }
xml file enable annotation configuration
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd "> <!--Annotation configuration enabled--> <context:annotation-config></context:annotation-config> <bean class="com.crab.spring.ioc.demo06.BeanOne" id="beanOne"> <!--It can be injected through conventional dependency injection--> </bean> <!--Dependencies are automatically injected through annotations--> <bean class="com.crab.spring.ioc.demo06.BeanTwo" id="beanTwo"/> </beans>
Get the bean s in the container for use
@org.junit.Test public void test_annotation_config() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo06/spring2.xml"); BeanTwo beanTwo = context.getBean(BeanTwo.class); System.out.println(beanTwo); context.close(); } // output BeanTwo{beanOne=com.crab.spring.ioc.demo06.BeanOne@491666ad}
Introduction to case 2
In entry case 1, the traditional xml configuration dependency injection method is simplified, but the bean definition information still needs to be manually configured in xml. The method of scanning beans can be further simplified by adding a configuration section.
<context:component-scan base-package="org.example"/>
Mark classes as bean s through annotations
@Component: indicates that the annotated class is "component". When using annotation based configuration and classpath scanning, this class is regarded as a candidate for automatic detection
@Component public class RepositoryA implements RepositoryBase { } @Component public class RepositoryB implements RepositoryBase { } @Component public class ServiceA { @Autowired private RepositoryA repositoryA; @Autowired private RepositoryB repositoryB; // Omit Getter toString() }
xml configuration scan package path
xml configuration file, very concise
<?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"> <!--Scan components under the specified package bean And automatically DI--> <context:annotation-config/> <context:component-scan base-package="com.crab.spring.ioc.demo06"/> <!--No more complicated bean Definition of--> </beans>
Get bean usage in container
The test procedure is similar to that in the previous article.
package com.crab.spring.ioc.demo06; /** * @author zfd * @version v1.0 * @date 2022/1/13 15:11 * @For me, please pay attention to the official account of crab Java notes for more technical series. */ public class Test { @org.junit.Test public void test() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo06/spring1.xml"); ServiceA serviceA = context.getBean(ServiceA.class); RepositoryA repositoryA = context.getBean(RepositoryA.class); RepositoryB repositoryB = context.getBean(RepositoryB.class); System.out.println(serviceA); System.out.println(serviceA.getRepositoryA() == repositoryA); System.out.println(serviceA.getRepositoryB() == repositoryB); context.close(); } }
Operation results
ServiceA{repositoryA=com.crab.spring.ioc.demo06.RepositoryA@27c86f2d, repositoryB=com.crab.spring.ioc.demo06.RepositoryB@197d671} true true
Conclusion: repository a, repository B and ServiceA are all scanned into the container for management, and the dependency of ServiceA has been automatically DI. Compared with the traditional XML method, this method is simple and worry-free.
Note: XML mode and annotation configuration mode can be mixed. Annotation injection is performed before XML injection. Therefore, XML configuration will override annotation injection.
@Required use
@The Required annotation is applicable to the bean property setting method. If the container does not have a corresponding bean, an exception will be thrown.
public class SimpleMovieLister { private MovieFinder movieFinder; @Required public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } }
@The Required annotation and RequiredAnnotationBeanPostProcessor have been officially discarded since Spring Framework 5.1. A better way is to use constructor injection (or the custom implementation of initializingbean. Afterpropertieset(), or the custom @ PostConstruct method and bean property setter method).
@Autowired uses
Mark the constructor, field, Setter method or configuration method as automatically assembled by Spring's dependency injection tool, and required specifies whether it is required. This is an alternative to the JSR-330 @Inject annotation.
Tag on constructor, field, Setter method
A class that combines three annotation positions
@Component public class Service1 { private RepositoryA repositoryA; private RepositoryB repositoryB; // Tag field @Autowired private RepositoryC repositoryC; // Tag constructor @Autowired public Service1(RepositoryA repositoryA) { this.repositoryA = repositoryA; } @Autowired public void setRepositoryB(RepositoryB repositoryB) { this.repositoryB = repositoryB; } @Override public String toString() { return "Service1{" + "repositoryA=" + repositoryA + ", repositoryB=" + repositoryB + ", repositoryC=" + repositoryC + '}'; } }
The test methods and results are as follows
@org.junit.Test public void test_autowired() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo06/spring1.xml"); Service1 bean = context.getBean(Service1.class); System.out.println(bean); context.close(); } // result Service1{repositoryA=com.crab.spring.ioc.demo06.RepositoryA@79efed2d, repositoryB=com.crab.spring.ioc.demo06.RepositoryB@2928854b, repositoryC=com.crab.spring.ioc.demo06.RepositoryC@27ae2fd0}
From the output results, the three location injection dependencies are all possible.
Starting with Spring Framework 4.3, if the target bean defines only one constructor, you no longer need to use the @ Autowired annotation on such constructors.
@Autowired injection set
Default order
All matching types in the container will be injected automatically, and the default order is the order of bean registration definition.
/** * @author zfd * @version v1.0 * @date 2022/1/15 17:48 * @For me, please pay attention to the official account of crab Java notes for more technical series. */ @Component public class Service2 { @Autowired private List<RepositoryBase> repositoryList; @Autowired private Set<RepositoryBase> repositorySet; @Autowired private RepositoryBase[] repositoryArr; // Omit Getter and Setter }
Tests and results
@org.junit.Test public void test_collection() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo06/spring1.xml"); Service2 service2 = context.getBean(Service2.class); System.out.println(service2.getRepositoryList()); System.out.println(service2.getRepositorySet()); Arrays.stream(service2.getRepositoryArr()).forEach(System.out::println); context.close(); }
[com.crab.spring.ioc.demo06.RepositoryA@2ddc8ecb, com.crab.spring.ioc.demo06.RepositoryB@229d10bd, com.crab.spring.ioc.demo06.RepositoryC@47542153] [com.crab.spring.ioc.demo06.RepositoryA@2ddc8ecb, com.crab.spring.ioc.demo06.RepositoryB@229d10bd, com.crab.spring.ioc.demo06.RepositoryC@47542153] com.crab.spring.ioc.demo06.RepositoryA@2ddc8ecb com.crab.spring.ioc.demo06.RepositoryB@229d10bd com.crab.spring.ioc.demo06.RepositoryC@47542153
From the result, the order is ABC.
Specify the order through @ Ordered
Add @ Ordered to repository a, repository B, and repository C. the smaller the value, the higher the priority.
@Component @Order(0) // Specifies the order in which collections are injected public class RepositoryA implements RepositoryBase { } @Component @Order(-1) public class RepositoryB implements RepositoryBase { } @Component @Order(-2) public class RepositoryC implements RepositoryBase{ }
Or run the above test_collection, observe the output order.
[com.crab.spring.ioc.demo06.RepositoryC@56a6d5a6, com.crab.spring.ioc.demo06.RepositoryB@309e345f, com.crab.spring.ioc.demo06.RepositoryA@7a4ccb53] [com.crab.spring.ioc.demo06.RepositoryA@7a4ccb53, com.crab.spring.ioc.demo06.RepositoryB@309e345f, com.crab.spring.ioc.demo06.RepositoryC@56a6d5a6] com.crab.spring.ioc.demo06.RepositoryC@56a6d5a6 com.crab.spring.ioc.demo06.RepositoryB@309e345f com.crab.spring.ioc.demo06.RepositoryA@7a4ccb53
From the results, the order is CBA, and the order is in line with expectations.
@Autowired injection Map
As long as the key type of Map is String, even typed Map instances can be assembled automatically.
Define a class, inject map and print
@Component public class Service3 { @Autowired private Map<String, RepositoryBase> repositoryMap; public void printMap() { this.repositoryMap.entrySet().forEach(entry -> { System.out.println(entry.getKey() + "--" + entry.getKey()); }); } }
Test the output
@org.junit.Test public void test_map() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo06/spring1.xml"); System.out.println("injection map The key value pairs are as follows:"); Service3 service3 = context.getBean(Service3.class); service3.printMap(); context.close(); }
injection map The key value pairs are as follows: repositoryA--repositoryA repositoryB--repositoryB repositoryC--repositoryC
From the results, it was successfully injected into the map.
@Autowired with @ Primary
@Primary indicates that when multiple candidates are eligible to automatically assemble single valued dependencies, the bean should be given priority.
Let's take a look at a case without @ Primary.
@Component @Order(0) // Specifies the order in which collections are injected public class RepositoryA implements RepositoryBase { } @Component @Order(-1) public class RepositoryB implements RepositoryBase { } @Component @Order(-2) public class RepositoryC implements RepositoryBase{ } @Component public class Service4 { @Autowired private RepositoryBase repositoryBase; @Override public String toString() { return "Service4{" + "repositoryBase=" + repositoryBase + '}'; } }
Since there are three repositorybases in the container, Spring cannot decide which one to choose, so it will throw unsatisfied dependencyexception as follows.
@org.junit.Test public void test_require() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo06/spring1.xml"); Service4 service4 = context.getBean(Service4.class); System.out.println(service4); context.close(); }
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'service4': Unsatisfied dependency expressed through field 'repositoryBase'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.crab.spring.ioc.demo06.RepositoryBase' available: expected single matching bean but found 3: repositoryA,repositoryB,repositoryC
Add repository C to @ Primary
@Component @Order(-2) @Primary public class RepositoryC implements RepositoryBase{ }
Run the above test again and successfully inject repository C.
Service4{repositoryBase=com.crab.spring.ioc.demo06.RepositoryC@4df50bcc}
@Autowired with Optional
@Autowired has a required attribute, which indicates whether the dependency is optional or not. The default is false. You can use Java util. Optional packaging.
/** * @author zfd * @version v1.0 * @date 2022/1/14 11:54 * @For me, please pay attention to the official account of crab Java notes for more technical series. */ @Component public class Service5 { private RepositoryA repositoryA; public RepositoryA getRepositoryA() { return repositoryA; } @Autowired public void setRepositoryA(Optional<RepositoryA> repositoryOptional) { RepositoryA repositoryA = repositoryOptional.orElseGet(() -> new RepositoryA()); this.repositoryA = this.repositoryA; } }
Starting from Spring Framework 5.0, the @ Nullable annotation can also be used to express the concept of nullability.
@Component public class Service6 { private RepositoryA repositoryA; @Autowired public void setRepositoryA(@Nullable RepositoryA repositoryA) { this.repositoryA = this.repositoryA; } }
Cooperate with @ Qualifier
As mentioned above, when there are multiple instances in a container and a Primary candidate needs to be determined, @ Primary is an effective method to use type automatic assembly, with multiple instances. When you need more control over the selection process, you can also use Spring's @ Qualifier annotation. By associating the Qualifier value with a specific parameter, the scope of type matching is reduced so that a specific bean can be selected for each parameter. Look at the case.
Quick use
Configuration of dependent classes
@Component @Order(0) public class RepositoryA implements RepositoryBase { } @Component("repositoryB") // Name specified @Order(-1) public class RepositoryB implements RepositoryBase { } @Component @Order(-2) @Primary // Mark as primary candidate public class RepositoryC implements RepositoryBase{ }
@Qualifier specifies the name of the bean on the field and constructor
@Component public class Service7 { // Specify to inject repository B @Autowired @Qualifier("repositoryB") private RepositoryBase repository; private RepositoryBase repository2; // Specify the injection of repositoryA in the constructor @Autowired public Service7(@Qualifier("repositoryA") RepositoryBase repository2) { this.repository2 = repository2; } // ellipsis }
Test it and observe the output.
@org.junit.Test public void test_qualifier() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo06/spring1.xml"); Service7 service7 = context.getBean(Service7.class); System.out.println(service7); context.close(); }
Service1{repository=com.crab.spring.ioc.demo06.RepositoryB@222114ba, repository2=com.crab.spring.ioc.demo06.RepositoryA@16e7dcfd}
Conclusion: RepositoryC is marked with @ Primary annotation, and such instances will be injected under normal circumstances. The @ Qualifier specifies that RepositoryB and RepositoryA are injected, and the verification is successful.
summary
This paper introduces the annotation based Spring container configuration, simplifies the preparation of xml configuration files, and provides two quick start cases. Focus on the analysis of @ Autowired with various annotations to flexibly inject dependency coverage scenarios. The next introduction introduces more annotations and classpath scanning on the basis of.
Source address of this article: https://github.com/kongxubihai/pdf-spring-series/tree/main/spring-series-ioc/src/main/java/com/crab/spring/ioc/demo06
Knowledge sharing, please indicate the source of reprint. There is no order in learning, and the one who reaches is the first!