Get a thorough understanding of Auto-assembly and Autoowired in Spring

Keywords: Spring Attribute Mybatis xml

Untitled

1. Automatic assembly

When Spring assembles Bean properties, it is sometimes quite clear that a reference to a Bean needs to be assembled to the specified properties.For example, if there is only one Bean of type org.mybatis.spring.SqlSessionFactoryBean in our application context, then any other Bean that relies on SqlSessionFactoryBean needs that Bean.After all, there is only one Bean for SqlSessionFactoryBean.
To address this clear assembly scenario, Spring provides autowiring.With its explicit assembly Bean property, why don't Spring recognize scenes that can be assembled automatically.
Spring has a number of ways to handle when it comes to the dependencies of automatically assembling beans.Therefore, Spring offers four automatic assembly strategies.

public interface AutowireCapableBeanFactory{
	//No need for automatic assembly
	int AUTOWIRE_NO = 0;
	//Automatically assemble bean properties by name
	int AUTOWIRE_BY_NAME = 1;
	//Automatically assemble bean properties by type
	int AUTOWIRE_BY_TYPE = 2;
	//Automatically assemble by constructor
	int AUTOWIRE_CONSTRUCTOR = 3;
	//Outdated method, no longer supported after Spring 3.0
	@Deprecated
	int AUTOWIRE_AUTODETECT = 4;
}
//Copy Code

Spring defines these policies in the AutowireCapableBeanFactory interface.Among them, AUTOWIRE_AUTODETECT is marked as obsolete and is no longer supported after Spring 3.0.

1,byName

It means that other beans with the same name as Bean's properties are automatically assembled into their corresponding properties.Sounds rather grumpy, let's take an example.
First, you have a property Role myRole in User's Bean, and then you create a Role's Bean whose name is myRole, then you can use byName in User to auto-assemble.

public class User{
	private Role myRole;
}
public class Role {
	private String id;	
	private String name;
}
//Copy Code

Above is the definition of Bean, then look at the configuration file.

<bean id="myRole" class="com.viewscenes.netsupervisor.entity.Role">
	<property name="id" value="1001"></property>
	<property name="name" value="Administrators"></property>
</bean>
<bean id="user" class="com.viewscenes.netsupervisor.entity.User" autowire="byName"></bean>
//Copy Code

As mentioned above, byName can be used to automate assembly in a user's Bean as long as the attribute name and the Bean name can correspond.So what if the attribute name doesn't correspond?

2,byType

Yes, you can also choose to use the type to auto-assemble without using the attribute name to correspond.It means that other beans of the same type as Bean's attributes are automatically assembled into their corresponding attributes.

<bean class="com.viewscenes.netsupervisor.entity.Role">
	<property name="id" value="1001"></property>
	<property name="name" value="Administrators"></property>
</bean>
<bean id="user" class="com.viewscenes.netsupervisor.entity.User" autowire="byType"></bean>
//Copy Code

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

3,constructor

It means that other beans of the same type are automatically assembled into the corresponding input parameters of the Bean constructor.Notice that other beans of the same type indicate whether they are determined by the type of bean when looking for a join.
The type of input in the constructor is 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 Code

4,autodetect

It first tries to use the constructor for auto-assembly and then byType if it fails.However, it has been marked @Deprecated since Spring 3.0.

5. Default Automatic Assembly

By default, the default-autowire property is set to none, indicating that all beans do not use automatic assembly unless the autowire property is configured on the beans.
If you need to configure the same autowire property for all beans, there is a way to simplify this.
Add the attribute default-autowire="byType" to the root element Beans.

<beans default-autowire="byType">
Copy Code

The advantages of Spring auto-assembly are self-evident.In fact, auto-assembly in the Spring XML configuration file is not recommended, and the biggest disadvantage in my opinion is uncertainty.Or unless you know exactly what's going on with all the beans in the entire Spring app, it can get worse as beans increase and relationship complexity increases.

2. Autowired

Starting with Spring 2.5, annotations have been supported to automatically assemble Bean properties.It allows finer-grained auto-assembly, and we can optionally label an attribute to apply auto-assembly to it.
Spring supports several different annotations for automatic assembly.

  • Spring comes with the @Autowired annotation.
  • JSR-330 @Inject comment.
  • The @Resource comment for JSR-250.

Today we are only focusing on the Autowired annotation, refer to the article in the author's Spring Source Series for its parsing and injection process. Spring Source Analysis (2) Instantiation of bean s and IOC Dependent Injection
Using @Autowired is easy, just add comments to the properties that need to be injected.

@Autowired
UserService userService;
Copy Code

However, there are a few points to note about using it.

1. Mandatory

By default, it has a mandatory contract feature, and the properties it labels must be assembled.If no Bean can be assembled into the properties or parameters labeled by Autowired, then you will see the exception information for NoSuchBean DefinitionException.

public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
			Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
	
	//Find Bean
	Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
	//If you get an empty collection of beans and isRequired, an exception is thrown.
	if (matchingBeans.isEmpty()) {
		if (descriptor.isRequired()) {
			raiseNoSuchBeanDefinitionException(type, "", descriptor);
		}
		return null;
	}
}
//Copy Code

Looking at the source above, we can get this information. It doesn't matter if the Bean collection is empty and the key isRequired condition doesn't hold, so if we're not sure if the attributes can be assembled, we can use Autowired in this way.

@Autowired(required=false)
UserService userService;
Copy Code

2. Assembly Strategy

I remember an interview asking: What strategy did Autowired follow to automatically assemble?
You can't generalize on this issue. You can't simply say by type or by name.But one thing you can be sure of is that it is automatically assembled by type by default, byType.

  • Assembly by type by default

Key point findAutowireCandidates method.

protected Map<String, Object> findAutowireCandidates(
		String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
	
	//Get the names of all beans of a given type, actually loop through all beanName s inside, and get an instance of it
	//The isTypeMatch method is then used to determine the
	String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
			this, requiredType, true, descriptor.isEager());
			
	Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length);
	
	//Get its instance return based on the returned beanName
	for (String candidateName : candidateNames) {
		if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) {
			result.put(candidateName, getBean(candidateName));
		}
	}
	return result;
}
//Copy Code
  • Assemble by name

You can see that it returns a list, which indicates that matching by type may result in multiple instances being queried.Which instance should I assemble?I've read articles that say you can avoid this by adding comments.For example, @qulifier, @Primary, and so on, there's actually a simple way.
For example, its implementation class is assembled according to the UserService interface type.The UserServiceInterface has several implementation classes, which are classified as UserServiceImpl, UserServiceImpl2.So when we inject, we can define the property name as the name of the Bean implementation class.

@Autowired
UserService UserServiceImpl2;
Copy Code

In this case, Spring will follow byName for assembly.First, if you look at multiple instances of a type, Spring has made a decision.

public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
			Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
			
	//Find Bean Instances by Type
	Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
	//Throw an exception if the Bean collection is empty and isRequired is established
	if (matchingBeans.isEmpty()) {
		if (descriptor.isRequired()) {
			raiseNoSuchBeanDefinitionException(type, "", descriptor);
		}
		return null;
	}
	//If more than one Bean instance is found
	if (matchingBeans.size() > 1) {
		//Find the right one, if not the right 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);
		}
		return matchingBeans.get(primaryBeanName);
	}	
}
//Copy Code

You can see that the determineAutowireCandidate method is the key if multiple instances are found.It determines an appropriate Bean return.Part of this is matching by Bean's name.

protected String determineAutowireCandidate(Map<String, Object> candidateBeans, 
				DependencyDescriptor descriptor) {
	//Bean collection looped to
	for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) {
		String candidateBeanName = entry.getKey();
		Object beanInstance = entry.getValue();
		//Determine if the name in the bean collection is the same as the name of the property using the matchesBeanName method
		if (matchesBeanName(candidateBeanName, descriptor.getDependencyName())) {
			return candidateBeanName;
		}
	}
	return null;
}
//Copy Code

Finally, when we get back to the question, the answer is: @Autowired uses byType by default to assemble attributes, and if multiple instances of a type are matched, byName is used to determine the Bean.

3. Priority and Priority

As we have seen above, Beans with multiple instances may be found through byType.Then use byName to determine a suitable Bean, if not by name?
Or determineAutowireCandidate, which has two ways to determine.

protected String determineAutowireCandidate(Map<String, Object> candidateBeans, 
				DependencyDescriptor descriptor) {
	Class<?> requiredType = descriptor.getDependencyType();
	//Identify Bean with @Primary annotation
	String primaryCandidate = determinePrimaryCandidate(candidateBeans, requiredType);
	if (primaryCandidate != null) {
		return primaryCandidate;
	}
	//Identify Bean value as a priority size with the @Priority(value = 0) annotation
	String priorityCandidate = determineHighestPriorityCandidate(candidateBeans, requiredType);
	if (priorityCandidate != null) {
		return priorityCandidate;
	}
	return null;
}
//Copy Code
  • Primary

Its purpose is to see if the @Primary annotation is included on the Bean and return if it is.Of course, you can't set multiple beans to @Primary, or you'll get the NoUniqueBeanDefinitionException 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 Code
  • Priority

You can also configure the @Priority annotation on the Bean, which has an int-type attribute value and can configure the priority size.The smaller the number, the better the match will be.Similarly, you cannot configure the priority of multiple beans to a value of the same size, otherwise the NoUniqueBean Definition Exception exception will come to 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 sizes are the same
				if (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 Code

Finally, there is one point to note.Priority's package is in javax.annotation.Priority; if you want to use it, you need to introduce a coordinate.

<dependency>
	<groupId>javax.annotation</groupId>
	<artifactId>javax.annotation-api</artifactId>
	<version>1.2</version>
</dependency>
//Copy Code

3. Summary

This chapter focuses on several strategies for auto-assembly in Spring, and analyzes the use of Autowired annotations through source code.
After Spring 3.0, there are three effective automatic assembly strategies: byType, byName, and constructor.Annotation Autowired uses byType by default to auto-assemble, try byName matching if there are multiple instances of a type, or Primary and Pririty annotations if byName is not sure.

Author: A Clear Place
Link: https://juejin.im/post/5c84b5285257c5b477177
Source: Excavation
Copyright belongs to the author.For commercial reprinting, please contact the author for authorization. For non-commercial reprinting, please indicate the source.

525 original articles were published. 2365 were praised. 2.18 million visits were received+
His message board follow

Posted by 990805 on Sat, 22 Feb 2020 16:17:00 -0800