First, automatic assembly

When Spring assembles Bean properties, it is sometimes very clear that you need to assemble a reference to a Bean into a specified property. For example, if we only had one application context org. Mybatis. Spring. SqlSessionFactoryBean type of Bean, then any dependent SqlSessionFactoryBean other beans is need the Bean. After all, there is only one Bean of SqlSessionFactoryBean.

To address this explicit assembly scenario, Spring provides autowiring. Instead of explicitly assembly Bean properties, why not let Spring recognize scenarios that can be auto-assembled?

Spring has several approaches when it comes to automating Bean dependencies. Therefore, Spring provides four autowiring strategies.

Public interface AutowireCapableBeanFactory {/ / without automatic assembly int AUTOWIRE_NO = 0; Int AUTOWIRE_BY_NAME = 1; Int AUTOWIRE_BY_TYPE = 2; Int AUTOWIRE_CONSTRUCTOR = 3; @deprecated int AUTOWIRE_AUTODETECT = 4 is Deprecated after Spring3.0; }Copy the code

Spring in AutowireCapableBeanFactory interface defines these strategies. Among them, AUTOWIRE_AUTODETECT is marked as obsolete and is no longer supported after Spring3.0.

1, byName

This means that other beans with the same name as the Bean’s properties are automatically assembled into the corresponding properties of the Bean. This might sound like a mouthful, but let’s look at an example.

First, there is an attribute Role myRole in the Bean of User, and then create a Bean of Role whose name is myRole, then the User can use byName to auto-assemble.

public class User{
	private Role myRole;
}
public class Role {
	private String id;	
	private String name;
}
Copy the code

Above is the Bean definition, and look at the configuration file.

<bean id="myRole" class="com.viewscenes.netsupervisor.entity.Role">
	<property name="id" value="1001"></property>
	<property name="name" value="Administrator"></property>
</bean>

<bean id="user" class="com.viewscenes.netsupervisor.entity.User" autowire="byName"></bean>
Copy the code

As mentioned above, byName can be used for auto-assembly in user’s Bean as long as the property name and Bean name correspond. So what if the property names don’t match?

2, byType

Yes, if you don’t use the property name for the corresponding, you can also choose to use the type for auto-assembly. This means that other beans of the same type as the Bean’s properties are automatically assembled into the corresponding properties of the Bean.

<bean class="com.viewscenes.netsupervisor.entity.Role">
	<property name="id" value="1001"></property>
	<property name="name" value="Administrator"></property>
</bean>

<bean id="user" class="com.viewscenes.netsupervisor.entity.User" autowire="byType"></bean>
Copy the code

As in the previous example, if byType is used, the ID of the Role Bean can be omitted.

3, the constructor

This means that other beans of the same type as the Bean’s constructor input parameter are automatically assembled into the corresponding Bean constructor input parameter. Value indicates that it is determined by the type of the Bean when looking for an input parameter.

The input parameter in the constructor is of type Role

public class User{
	private Role role;

	public User(Role role) {
		this.role = role;
	}
}

<bean id="user" class="com.viewscenes.netsupervisor.entity.User" autowire="constructor"></bean>
Copy the code

4, autodetect

It first tries to autowire using Constructor, and if that fails, tries byType. However, it has been marked @deprecated since Spring3.0.

5. Automatic assembly by default

By default, the default-Autowire property is set to None, indicating that all beans do not use autowire unless the Bean is configured with the Autowire property. If you need to configure the same Autowire property for all your beans, there is a way to simplify this. Add the attribute default-Autowire =”byType” to the root Beans.

<beans default-autowire="byType">
Copy the code

The advantages of Spring autowiring are self-evident. But in fact, autoroing in Spring XML configuration files is not recommended, and I think the biggest disadvantage is uncertainty. Or unless you know what’s going on with all the beans in your entire Spring application, things can get ugly as beans grow and relationship complexity increases.

Second, Autowired

Since Spring2.5, support has been provided for using annotations to automatically assemble Bean properties. It allows for more fine-grained autologization, whereby we can selectively annotate a property to apply autologization to it.

Spring supports several different annotations for autowiring.

  • Spring comes with the @Autowired annotation.

  • Annotate @Inject of JSR-330.

  • Jsr-250 @Resource annotation.

Today we will focus only on the Autowired annotation, which is parsed and injected in my Spring source code series. Spring source code analysis (2) bean instantiation and IOC dependency injection

Using @AutoWired is as simple as adding annotations to properties that need to be injected.

@Autowired
UserService userService;
Copy the code

However, there are a few caveats to using it.

1. Mandatory

By default, it has a mandatory contract nature, and the attributes it annotates must be assemblable. If there is no Bean can be assembled into Autowired with properties or parameters, then you will see the NoSuchBeanDefinitionException exception information.

public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
			Set<String> autowiredBeanNames, TypeConverter typeThrows BeansException {// Find Bean Map<String, Object> matchingBeans = findAutowireCandidates(beanName,type, descriptor); // If the Bean set is empty and isRequired, throw an exception.if (matchingBeans.isEmpty()) {
		if (descriptor.isRequired()) {
			raiseNoSuchBeanDefinitionException(type."", descriptor);
		}
		returnnull; }}Copy the code

Looking at the source code above, we can get the message that it does not matter if the Bean collection is empty, the key isRequired condition does not hold, so if we are not sure whether the property can be assembled, we can use Autowired in this way.

@Autowired(required=false)
UserService userService;
Copy the code

2. Assembly strategy

I remember there was an interview question that asked: What strategy does Autowired use for auto-assembly?

You can’t generalize about this, you can’t just say by type or by name. One thing you can be sure of, however, is that it is autoassembled byType by default, that is, byType.

  • The default assembly is by type

Key point findAutowireCandidates method.

protected Map<String, Object> findAutowireCandidates( String beanName, Class<? > requiredType, DependencyDescriptor) {// Get the beanName of the specified type. To obtain its instance / / by isTypeMatch method to determine the String [] candidateNames = BeanFactoryUtils. BeanNamesForTypeIncludingAncestors (this, requiredType,true, descriptor.isEager()); Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length); // Based on the returned beanName, get the instance returnfor (String candidateName : candidateNames) {
		if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) {
			result.put(candidateName, getBean(candidateName));
		}
	}
	return result;
}
Copy the code
  • Assemble by name

You can see that it returns a list, indicating that multiple instances can be queried by type matching. Which instance should you assemble? I read in some articles that you can add annotations to get around this. For example, @qulifier, @primary, etc., there is an easy way.

For example, assemble its implementation class according to the UserService interface type. The UserService interface has multiple implementation classes, including UserServiceImpl and UserServiceImpl2. Then we can define the property name as the name of the Bean implementation class at injection time.

@Autowired
UserService UserServiceImpl2;
Copy the code

In this case, Spring will do the assembly according to byName. First, if you find multiple instances of a type, Spring has already made a judgment.

public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
			Set<String> autowiredBeanNames, TypeConverter typeMap<String, Object> matchingBeans = findAutowireCandidates(beanName,type, descriptor); // Throw an exception if the Bean collection is empty and isRequired is trueif (matchingBeans.isEmpty()) {
		if (descriptor.isRequired()) {
			raiseNoSuchBeanDefinitionException(type."", descriptor);
		}
		returnnull; } // If you are looking for more than one Bean instanceif(matchingbeans.size () > 1) {// Find the most suitable one if there is no suitable one. Also throw an exception String primaryBeanName = determineAutowireCandidate (matchingBeans, descriptor);if (primaryBeanName == null) {
			throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
		}
		if(autowiredBeanNames ! = null) { autowiredBeanNames.add(primaryBeanName); }returnmatchingBeans.get(primaryBeanName); }}Copy the code

As you can see, if find multiple instances of determineAutowireCandidate method is the key. It determines an appropriate Bean to return. Part of this is matching by Bean name.

protected String determineAutowireCandidate(Map<String, Object> candidateBeans, DependencyDescriptor) {// Set of beans to be recycledfor(Map.Entry<String, Object> entry : candidateBeans.entrySet()) { String candidateBeanName = entry.getKey(); Object beanInstance = entry.getValue(); // Use the matchesBeanName method to determine whether the name in the bean collection is the same as the name of the propertyif (matchesBeanName(candidateBeanName, descriptor.getDependencyName())) {
			returncandidateBeanName; }}return null;
}
Copy the code

Finally, we come back to the problem and get the answer: @AutoWired defaults to using byType to assemble properties, and byName to identify beans if multiple instances of the type are matched.

3. Master and priority

As we saw above, it is possible to find multiple instances of beans through byType. ByName is then used to determine an appropriate Bean. What if you can’t determine a Bean byName? Or determineAutowireCandidate this method, it also has two ways to determine.

protected String determineAutowireCandidate(Map<String, Object> candidateBeans, DependencyDescriptor descriptor) { Class<? > requiredType = descriptor.getDependencyType(); / / by @ Primary annotation to identify Bean String primaryCandidate = determinePrimaryCandidate (candidateBeans requiredType);if(primaryCandidate ! = null) {returnprimaryCandidate; } // The Bean value is identified by the @priority (value = 0) annotation String priorityCandidate = determineHighestPriorityCandidate(candidateBeans, requiredType);if(priorityCandidate ! = null) {return priorityCandidate;
	}
	return null;
}
Copy the code
  • Primary

It looks to see if the Bean contains the @primary annotation and returns if it does. Of course, you can set multiple beans to @ Primary, or you’ll get NoUniqueBeanDefinitionException this exception.

protected String determinePrimaryCandidate(Map<String, Object> candidateBeans, Class<? > requiredType) { String primaryBeanName = null;for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) {
		String candidateBeanName = entry.getKey();
		Object beanInstance = entry.getValue();
		if (isPrimary(candidateBeanName, beanInstance)) {
			if(primaryBeanName ! = null) { boolean candidateLocal = containsBeanDefinition(candidateBeanName); boolean primaryLocal = containsBeanDefinition(primaryBeanName);if (candidateLocal && primaryLocal) {
					throw new NoUniqueBeanDefinitionException(requiredType, candidateBeans.size(),
							"more than one 'primary' bean found among candidates: " + candidateBeans.keySet());
				}
				else if(candidateLocal) { primaryBeanName = candidateBeanName; }}else{ primaryBeanName = candidateBeanName; }}}return primaryBeanName;
}
Copy the code
  • Priority

You can also configure the @priority annotation on the Bean, which has an attribute of type int value and can set the Priority size. The smaller the number, the better the match. The same, you also can’t more than the Bean priority configured to the size of the same value, or abnormal NoUniqueBeanDefinitionException out looking for you.

protected String determineHighestPriorityCandidate(Map<String, Object> candidateBeans, Class<? > requiredType) { String highestPriorityBeanName = null; Integer highestPriority = null;for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) {
		String candidateBeanName = entry.getKey();
		Object beanInstance = entry.getValue();
		Integer candidatePriority = getPriority(beanInstance);
		if(candidatePriority ! = null) {if(highestPriorityBeanName ! = null) {// If the priority size is the sameif (candidatePriority.equals(highestPriority)) {
					throw new NoUniqueBeanDefinitionException(requiredType, candidateBeans.size(),
						"Multiple beans found with the same priority ('" + highestPriority + "')" +
							"among candidates: " + candidateBeans.keySet());
				}
				else if(candidatePriority < highestPriority) { highestPriorityBeanName = candidateBeanName; highestPriority = candidatePriority; }}else{ highestPriorityBeanName = candidateBeanName; highestPriority = candidatePriority; }}}return highestPriorityBeanName;
}
Copy the code

Finally, there is one caveat. Priority package in javax.mail. The annotation. Priority; If you want to use it, you have to introduce a coordinate.

< < the dependency > < groupId > javax.mail. An annotation/groupId > < artifactId > javax.mail. The annotation - API < / artifactId > < version > 1.2 < / version > </dependency>Copy the code

Third, summary

This chapter focuses on several strategies of automatic assembly in Spring, and analyzes the use of Autowired annotations through source code. After Spring3.0, effective autowiring strategies can be divided into byType, byName and constructor. The annotation Autowired defaults to using byType for auto-assembly. If there are multiple instances of a type, try to use byName matching. If you cannot determine byName, use the Primary and Priority annotations.