Thoroughly understand the automatic assembly in Spring and the use of Autowired annotations

  • 2021-07-06 10:54:31
  • OfStack

STEP 1 Automatic assembly

When an Spring assembles an Bean attribute, it is sometimes very clear that a reference to an Bean needs to be assembled to the specified attribute. For example, if there is only one Bean of type org. mybatis. spring. SqlSessionFactoryBean in our application context, then any one other Bean that depends on SqlSessionFactoryBean needs this Bean. After all, there is only one Bean for SqlSessionFactoryBean.

To cope with this explicit assembly scenario, Spring provides automatic assembly (autowiring). Instead of explicitly assembling Bean, why not let Spring identify scenes that can be assembled automatically?

When it comes to automated assembly of Bean dependencies, Spring can be handled in a number of ways. Therefore, Spring provides four automatic assembly strategies.


public interface AutowireCapableBeanFactory{

 // No need for automatic assembly 
 int AUTOWIRE_NO = 0;

 // Automatic assembly by name bean Attribute 
 int AUTOWIRE_BY_NAME = 1;

 // Automatic assembly by type bean Attribute 
 int AUTOWIRE_BY_TYPE = 2;

 // Automatic assembly by constructor 
 int AUTOWIRE_CONSTRUCTOR = 3;

 // Outdated methods, Spring3.0 After that, it is no longer supported 
 @Deprecated
 int AUTOWIRE_AUTODETECT = 4;
}

Spring in AutowireCapableBeanFactory These policies are defined in the interface. Among them, AUTOWIRE_AUTODETECT Is marked as obsolete and is no longer supported after Spring 3.0.

1. byName

It means that other Bean with the same name as Bean's attribute is automatically assembled into the corresponding attribute of Bean. It may sound awkward. Let's take an example.

First, there is an attribute Role myRole in Bean of User, and then an Bean of Role is created. If its name is myRole, byName can be used for automatic assembly in User.


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

The above is the definition of Bean, and 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=" Administrator "></property>
</bean>

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

As mentioned above, byName can be used for automatic assembly in Bean of user as long as the attribute name and the name of Bean can correspond. So, what if the attribute name doesn't match?

2. byType

Yes, if you don't use attribute names for matching, you can also choose to use types for automatic assembly. It means that other Bean of the same type as the Bean's attribute is automatically assembled into the corresponding attribute of 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>

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

3. constructor

This means that other Bean of the same type as the Bean constructor input parameters are automatically assembled into the corresponding input parameters of the Bean constructor. Note that other Bean of the same type indicates that it is determined by the type of Bean when looking for input parameters.
The type of parameter entered 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>

4. autodetect

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

5. Default automatic assembly

By default, the default-autowire property is set to none, indicating that all Bean do not use automatic assembly unless the autowire property is configured on Bean.
If you need to configure the same autowire properties for all Bean, there is a way to simplify this operation.

Adding Attributes to Root Element Beans default-autowire="byType"。


<beans default-autowire="byType">

The advantages of Spring automatic assembly are self-evident. However, in fact, automatic assembly in Spring XML configuration file is not recommended, and the biggest disadvantage is uncertainty. Or unless you know all the Bean in the whole Spring application, the situation may be very bad with the increase of Bean and the increase of relationship complexity

2. Autowired

Beginning with Spring 2.5, support for automating the assembly of Bean properties using annotations began. It allows for more fine-grained automatic assembly, and we can selectively label a certain attribute to apply automatic assembly to it.

Spring supports several different annotations for automatic assembly.

Spring comes with @ Autowired annotation. @ Inject annotation for JSR-330. @ Resource annotation for JSR-250.

Today, we only focus on Autowired annotation. Please refer to the author's Spring source code series for its parsing and injection process. Spring Source Analysis (2) Instantiation of bean and IOC Dependency Injection

Using @ Autowired is very simple, just add annotations to the attributes that need to be injected.


@Autowired
UserService userService;

However, there are several points to note when using it.

1. Mandatory

By default, it has a mandatory contract feature, and the properties it labels must be assemblable. If no Bean can be assembled into the attributes or parameters marked by Autowired, then you will see that NoSuchBeanDefinitionException Gets or sets the exception information for.


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 it, Bean Collection is empty, and isRequired The exception is thrown. 
 if (matchingBeans.isEmpty()) {
 if (descriptor.isRequired()) {
  raiseNoSuchBeanDefinitionException(type, "", descriptor);
 }
 return null;
 }
}

Seeing the above source code, we can get this information. It doesn't matter if the Bean set is empty, and the key isRequired condition can't be established. Then, if we are not sure whether the attribute can be assembled, we can use Autowired in this way.


@Autowired(required=false)
UserService userService;

2. Assembly strategy

I remember an interview question once asked: What strategy is Autowired automatically assembled?
On this issue, you can't generalize, you can't simply say by type or by name. But one thing that is certain is that it is automatically assembled by type by default, that is, byType.

Assemble by type by default

Key point findAutowireCandidates this method.


protected Map<String, Object> findAutowireCandidates(
 String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
 
 // Gets all of the given type bean Name, which actually loops all the beanName Gets an instance of it 
 // Re-pass isTypeMatch Method to determine 
 String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
  this, requiredType, true, descriptor.isEager());
  
 Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length);
 
 // Based on the returned beanName Gets the return of its instance 
 for (String candidateName : candidateNames) {
 if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) {
  result.put(candidateName, getBean(candidateName));
 }
 }
 return result;
}

Assemble by name

You can see that it returns a list, which indicates that multiple instances may be queried by type matching. Which instance should be assembled? I read that some articles say that annotations can be added to avoid it. For example, @ qulifier, @ Primary, etc., there is actually a simple way.

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


public class User{
 private Role myRole;
}
public class Role {
 private String id; 
 private String name;
}
0

In this case, Spring will be assembled according to byName. First, if multiple instances of the type are found, Spring has already made a judgment.


public class User{
 private Role myRole;
}
public class Role {
 private String id; 
 private String name;
}
1

As you can see, if multiple instances are found, determineAutowireCandidate Method is the key. It comes to determine a suitable Bean return. One part is to match according to the name of Bean.


public class User{
 private Role myRole;
}
public class Role {
 private String id; 
 private String name;
}
2

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

3. Master and Priority

As we have seen above, it is possible to find multiple instances of Bean through byType. Then determine a suitable Bean by byName. What if you can't determine it by name?

Or determineAutowireCandidate This method, it has two ways to determine.


protected String determineAutowireCandidate(Map<String, Object> candidateBeans, 
  DependencyDescriptor descriptor) {
 Class<?> requiredType = descriptor.getDependencyType();
 // Pass @Primary Annotation to identify Bean
 String primaryCandidate = determinePrimaryCandidate(candidateBeans, requiredType);
 if (primaryCandidate != null) {
 return primaryCandidate;
 }
 // Pass @Priority(value = 0) Annotation to identify Bean value Is the priority size 
 String priorityCandidate = determineHighestPriorityCandidate(candidateBeans, requiredType);
 if (priorityCandidate != null) {
 return priorityCandidate;
 }
 return null;
}

Primary

Its function is to see if the @ Primary annotation is included on Bean, and return if so. Of course, you can't set multiple Bean to @ Primary, or you will get NoUniqueBeanDefinitionException This anomaly.


public class User{
 private Role myRole;
}
public class Role {
 private String id; 
 private String name;
}
4

Priority

You can also configure the @ Priority annotation on Bean, which has an attribute value of type int and can configure the priority size. The smaller the number, the priority is given to matching. Similarly, you can't configure the priority of multiple Bean to the same size, otherwise NoUniqueBeanDefinitionException Abnormal still came out to find you.


public class User{
 private Role myRole;
}
public class Role {
 private String id; 
 private String name;
}
5

Finally, there is one point to note. The package of Priority is in javax. annotation. Priority; If you want to use it, you also need to introduce 1 coordinate.


public class User{
 private Role myRole;
}
public class Role {
 private String id; 
 private String name;
}
6

3. 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, there are three effective automatic assembly strategies: byType, byName and constructor. Annotation Autowired uses byType for automatic assembly by default. If there are multiple instances of the type, try to use byName matching. If it is not determined by byName, it can be determined by Primary and Priority annotations.


Related articles: