Spring source code analysis of the properties of injection

An automatic assembly

When assembling Spring Bean properties, sometimes very clear, that you need to assemble a reference to the specified properties of a Bean. For example, if our application context is only one org.mybatis.spring.SqlSessionFactoryBeantype of Bean, then rely on any one SqlSessionFactoryBeanof the other is the need for the Bean Bean. After all, there is only one SqlSessionFactoryBeanof Bean.

In response to this explicit scene assembly, Spring provides automatic assembly (autowiring). Its explicit assembly Bean property, why not Spring scene is recognized automatically assembled.

When it comes to the automated assembly dependencies Bean, Spring a variety of treatments. Therefore, Spring provides four automatic assembly strategy.

public  interface AutowireCapableBeanFactory { 

    // Do not automatically assembling 
    int AUTOWIRE_NO = 0 ; 

    // by automatic assembly bean property name 
    int AUTOWIRE_BY_NAME =. 1 ; 

    // by type automatic assembly bean property 
    int AUTOWIRE_BY_TYPE = 2 ; 

    // constructed in automatic assembly 
    int AUTOWIRE_CONSTRUCTOR = 3 ; 

    // outdated method, is no longer supported after Spring3.0 
    @Deprecated
     int AUTOWIRE_AUTODETECT. 4 = ; 
}

Spring in the AutowireCapableBeanFactorydefinition of these types of policy interface. Which AUTOWIRE_AUTODETECTis marked as obsolete method is no longer supported after Spring3.0.

1、byName

It means that the other Bean and Bean attributes with the same name to the corresponding automatic assembly of Bean property. May sound hard to pronounce, we look at an example.

First of all, there are in the User's Bean in a property Role myRole, and then create a Role Bean, its name if called myRole, then you can use the User byName in to automatic assembly.

public class User{
    private Role myRole;
}
public class Role {
    private String id;  
    private String name;
}
Bean is defined above, look at the configuration file.
<bean id="myRole" class="com.viewscenes.netsupervisor.entity.Role">
    <property name="id" value="1001"></property>
    <property name="name" value="管理员"></property>
</bean>

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

As described above, as long as the property name and the names may correspond Bean, you can be used to automatically fit byName user of the Bean. So, if the property does not correspond to the name on it?

2、byType

Yes, if property name does not correspond to, you can choose the type used to automatically assemble. It means that the other having the same type of Bean Bean properties to the corresponding properties of automatic assembly of Bean.

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

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

Or the above example, if you use byType, Role Bean's ID are omitted.

3、constructor

It is said that the Bean and other parameters into the constructor Bean automatic assembly of the same type to the corresponding Bean constructor into ginseng. Note the value that other Bean has the same type of sentence that it look into the parameters of time, or is determined by the type of Bean.

Constructor parameters into the Role of type

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 the constructor for automatic assembly, if that fails then try to use byType. However, after it has been marked as Spring3.0 @Deprecated.

5, the default automatic assembly

By default, default-autowire attribute is set to none, all labeled Bean not use automated assembly, unless the properties of the autowire Bean arranged.
If you need to configure the same property for all autowire Bean, there is a way to simplify this operation.
Increasing property on the root element Beans default-autowire="byType".

<beans default-autowire="byType">

Spring assembly automatically self-evident advantages. But in fact, automatic assembly in Spring XML configuration file is not recommended, which I think the biggest drawback is that uncertainty. Or unless you get the whole Spring application of all Bean, otherwise increase with the rising complexity of the relationship and Bean, the situation may be very bad.

二、Autowired

Spring2.5 from the beginning, start to support the use of automatic assembly annotation Bean attributes. It allows for a more granular automatic assembly, we could selectively label a certain property to automatic assembly applied thereto.

Spring supports several different notes used in automatic assembly.

  • Spring comes @Autowired comment.

  • The JSR-330 @Inject annotations.

  • JSR-250 annotations of @Resource.

@Autowired very simple to use, the need to inject the properties can add comments.

@Autowired
UserService userService;

However, the use of which there are several points to note.

1, Mandatory

By default, it is mandatory contract characteristics, it must be marked attributes assembled. Without Bean can be fitted to Autowired marked attributes or parameters, then you will see NoSuchBeanDefinitionExceptioninformation about the exception.

public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
            Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
    
    //查找Bean
    Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
    //如果拿到的Bean集合为空,且isRequired,就抛出异常。
    if (matchingBeans.isEmpty()) {
        if (descriptor.isRequired()) {
            raiseNoSuchBeanDefinitionException(type, "", descriptor);
        }
        return null;
    }
}
See the above code, we can get this information, Bean set is empty does not matter, the key isRequired conditions are not established, then, if we are not sure whether the property can be assembled, it can be used Autowired.
@Autowired(required=false)
UserService userService;

2, assembly strategy

I remember there was such a face questions are asked: Autowired is in accordance with what strategies to automatically assemble it?

On this issue, it can not be generalized, you can not simply say by type or by name. But it is certain that it is, it is in accordance with the default type of automatic assembly, i.e. byType.

  • Assembly according to the default type

The key point of findAutowireCandidatesthis method.

protected the Map <String, Object> findAutowireCandidates ( 
        String the beanName, Class <?> requiredType, DependencyDescriptor descriptor) { 
    
    // Get the name given to all bean types, all of which actual cycle the beanName, examples of which acquired
     // through the method isTypeMatch determining 
    String [] = candidateNames BeanFactoryUtils.beanNamesForTypeIncludingAncestors (
             the this , requiredType, to true , descriptor.isEager ()); 
            
    the Map <String, Object> = Result new new a LinkedHashMap <String, Object> (candidateNames.length); 
    
    // the return beanName, examples of obtaining returns 
    for (String candidateName: candidateNames) {
         IF (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) {
            result.put(candidateName, getBean(candidateName));
        }
    }
    return result;
}
  • Assembly by name

You can see it returns a list, then it shows that, according to the type of match can query multiple instances. In the end which instance should assemble it? I see some article that can be annotated in order to circumvent. For example @qulifier、@Primary, etc., actually there is a simple solution.

For example, the assembly according to the interface type UserService its implementation class. UserService interface has multiple implementation classes, divided UserServiceImpl、UserServiceImpl2. So when we inject, you can define the attribute name is the name of Bean implementation class.

@Autowired
UserService UserServiceImpl2;
In this case, Spring will be assembled in accordance with byName. First of all, if multiple instances of the type found, Spring has made a judgment.
public Object doResolveDependency (DependencyDescriptor descriptor, the beanName String, 
            the Set <String> autowiredBeanNames, the TypeConverter TypeConverter) throws BeansException { 
            
    // find Bean instance according to the type of 
    the Map <String, Object> = matchingBeans findAutowireCandidates (the beanName, type, descriptor);
     // if Bean the collection is empty, and the establishment of isRequired throw an exception 
    IF (matchingBeans.isEmpty ()) {
         IF (descriptor.isRequired ()) { 
            raiseNoSuchBeanDefinitionException (of the type, "" , descriptor); 
        } 
        return  null ; 
    } 
    // If you are looking for Bean example 1 is greater than 
    if(matchingBeans.size ()> 1 ) {
         // find the most suitable one, if not appropriate. . Also thrown 
        String primaryBeanName = determineAutowireCandidate (matchingBeans, descriptor);
         IF (primaryBeanName == null ) {
             the throw  new new NoUniqueBeanDefinitionException (type, matchingBeans.keySet ()); 
        } 
        IF (! AutowiredBeanNames = null ) { 
            autowiredBeanNames.add (primaryBeanName) ; 
        } 
        return matchingBeans.get (primaryBeanName); 
    }    
}
As can be seen, if found in more than one instance, determineAutowireCandidate the method is the key. It is used to determine an appropriate return Bean. Some of which is in accordance with Bean's name to match.
protected String determineAutowireCandidate (the Map <String, Object> candidateBeans, 
                DependencyDescriptor descriptor) { 
    // cycle Bean get set 
    for (of Map.Entry <String, Object> entry: candidateBeans.entrySet ()) { 
        String candidateBeanName = entry.getKey ( ); 
        Object beanInstance = entry.getValue ();
         // determined by matchesBeanName bean collection method name is identical to the name of the attribute 
        iF (matchesBeanName (candidateBeanName, descriptor.getDependencyName ())) {
             return candidateBeanName; 
        } 
    } 
    return  null ; 
}

Finally, we return to the question, the answer is: @Autowired default byType to assemble property, if matched to multiple instances of the type, and then to determine the Bean by byName.

3, the primary priority

Above we have seen, by byType you may find multiple instances of Bean. And then to determine a suitable Bean by byName, by name if it can not determine it?
Or determineAutowireCandidatethis method, there are two ways to determine it.

protected String determineAutowireCandidate(Map<String, Object> candidateBeans, 
                DependencyDescriptor descriptor) {
    Class<?> requiredType = descriptor.getDependencyType();
    //通过@Primary注解来标识Bean
    String primaryCandidate = determinePrimaryCandidate(candidateBeans, requiredType);
    if (primaryCandidate != null) {
        return primaryCandidate;
    }
    //通过@Priority(value = 0)注解来标识Bean value为优先级大小
    String priorityCandidate = determineHighestPriorityCandidate(candidateBeans, requiredType);
    if (priorityCandidate != null) {
        return priorityCandidate;
    }
    return null;
}
  • Primary

Its role is to see if it contains notes on @Primary Bean, if included on the return. Of course, you can not put more Bean are set to @Primary, or you'll get NoUniqueBeanDefinitionExceptionthis 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;
}
  • Priority

You can also configure on Bean @Priority notes, it has a property of type int value, you can configure the priority size. The smaller the number, it is preferentially matched. Similarly, you can not configure multiple Bean priority to the value of the same size, or NoUniqueBeanDefinitionExceptionabnormal still 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 (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;
}
Finally, there is one caveat. Priority of the package javax.annotation.Priority; , if you want to use it also introduced a coordinate.
<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.2</version>
</dependency>

Guess you like

Origin www.cnblogs.com/laoxia/p/11040223.html