Spring-dependency injection findAutowireCandidates source code implementation

findAutowireCandidates() implementation

1. Find the names of all beans of type type in the BeanFactory. Based on the BeanDefinition, you can determine whether they match the current type without generating a Bean object.

2. Find the object whose key is type in resolvableDependencies and add it to result.

3. Traverse the beanName found based on type and determine whether the bean corresponding to the current beanName can be automatically injected.

4. First judge the autowireCandidate attribute in the BeanDefinition corresponding to beanName. If it is false, it means that it cannot be used for automatic injection. If it is true, continue to judge.

5. Determine whether the current type is generic. If it is generic, all beanNames in the container will be found. If this is the case, then in this step, the real type of the generic must be obtained, and then matched. , if the current beanName matches the real type corresponding to the current generic, then continue to judge

6. If the @Qualifier annotation exists on the current DependencyDescriptor, then it is necessary to determine whether the Qualifier is defined on the current beanName and whether it is equal to the Qualifier on the current DependencyDescriptor. If equal, it will match.

7. After the above verification, the current beanName can become an injectable and added to the result.

/**
 * Find bean instances that match the required type.
 * Called during autowiring for the specified bean.
 * @param beanName the name of the bean that is about to be wired
 * @param requiredType the actual type of bean to look for
 * (may be an array component type or collection element type)
 * @param descriptor the descriptor of the dependency to resolve
 * @return a Map of candidate names and candidate instances that match
 * the required type (never {@code null})
 * @throws BeansException in case of errors
 * @see #autowireByType
 * @see #autowireConstructor
 */
protected Map<String, Object> findAutowireCandidates(
@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

// Find the beanName that matches the requiredType from the BeanFactory. It is just the beanName. These beans may not have been instantiated. Only when a bean is finally determined, if the bean has not been instantiated, will it actually be instantiated.
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this, requiredType, true, descriptor.isEager());
Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);

// Match beans from resolvableDependencies according to type. ResolvableDependencies stores type: Bean objects, such as BeanFactory.class:BeanFactory objects, which are set when Spring starts.
for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
Class<?> autowiringType = classObjectEntry.getKey();
if (autowiringType.isAssignableFrom(requiredType)) {
Object autowiringValue = classObjectEntry.getValue();
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);

if (requiredType.isInstance(autowiringValue)) {
result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
break;
}
}
}


for (String candidate : candidateNames) {
// If it is not yourself, determine whether the candidate can be used for automatic injection.
if (!isSelfReference(beanName, candidate) & amp; & amp; isAutowireCandidate(candidate, descriptor)) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}

// If it is empty, it means there is really no match, or it matches itself.
if (result.isEmpty()) {
// Whether the type to be matched is Map, array, etc.
boolean multiple = indicatesMultipleBeans(requiredType);
// Consider fallback matches if the first pass failed to find anything...
DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
for (String candidate : candidateNames) {
if (!isSelfReference(beanName, candidate) & amp; & amp; isAutowireCandidate(candidate, fallbackDescriptor) & amp; & amp;
(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}

// What matches is itself and is added to the result by itself.
if (result.isEmpty() & amp; & amp; !multiple) {
// Consider self references as a final pass...
// but in the case of a dependency collection, not the very same bean itself.
for (String candidate : candidateNames) {
if (isSelfReference(beanName, candidate) & amp; & amp;
(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) & amp; & amp;
isAutowireCandidate(candidate, fallbackDescriptor)) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
}
}
return result;
}

Implementation of generic injection in dependency injection

First of all, in Java reflection, there is a Type interface, which represents the type. The specific classification is:

1. Raw types: that is, ordinary Class

2. parameterized types: corresponding to the ParameterizedType interface, generic type

3. Array types: corresponds to GenericArrayType, generic array

4. Type variables: Corresponds to the TypeVariable interface, representing type variables, which are defined generics, such as T, K

5. Primitive types: basic types, int, boolean

public class TypeTest<T>
{
    private int i;
    
    private Integer it;
    
    private int[] iarray;
    
    private List list;
    
    private List<String> slist;
    
    private List<T> tlist;
    
    private T t;
    
    private T[] tarray;
    
    public static void main(String[] args) throws NoSuchFieldException
    {
        test(TypeTest.class.getDeclaredField("i"));
        System.out.println("========");
        test(TypeTest.class.getDeclaredField("it"));
        System.out.println("========");
        test(TypeTest.class.getDeclaredField("iarray"));
        System.out.println("========");
        test(TypeTest.class.getDeclaredField("list"));
        System.out.println("========");
        test(TypeTest.class.getDeclaredField("slist"));
        System.out.println("========");
        test(TypeTest.class.getDeclaredField("tlist"));
        System.out.println("========");
        test(TypeTest.class.getDeclaredField("t"));
        System.out.println("========");
        test(TypeTest.class.getDeclaredField("tarray"));
        
    }
    
    public static void test(Field field)
    {
        if (field.getType().isPrimitive())
        {
            System.out.println(field.getName() + "is a basic data type");
        }
        else
        {
            System.out.println(field.getName() + "not a basic data type");
        }
        
        if (field.getGenericType() instanceof ParameterizedType)
        {
            System.out.println(field.getName() + "is a generic type");
        }
        else
        {
            System.out.println(field.getName() + "Not a generic type");
        }
        
        if (field.getType().isArray())
        {
            System.out.println(field.getName() + "is an ordinary array");
        }
        else
        {
            System.out.println(field.getName() + "not an ordinary array");
        }
        
        if (field.getGenericType() instanceof GenericArrayType)
        {
            System.out.println(field.getName() + "is a generic array");
        }
        else
        {
            System.out.println(field.getName() + "Not a generic array");
        }
        
        if (field.getGenericType() instanceof TypeVariable)
        {
            System.out.println(field.getName() + "is a generic variable");
        }
        else
        {
            System.out.println(field.getName() + "Not a generic variable");
        }
    }
}

The injection point in Spring is generic:

@Component
public class UserService extends BaseService<OrderService, StockService>
{
    public void test()
    {
        System.out.println(o);
    }
}

public class BaseService<O, S>
{
    @Autowired
    protected O o;

    @Autowired
    protected S s;
}

1. Spring scan found that UserService is a Bean

2. Take out the injection point, which is the two attributes o and s in BaseService.

3. Inject according to the injection point type and find that o and s are both generic, so Spring needs to determine the specific types of o and s.

4. What is currently being created is the Bean of UserService, so you can get specific generic information through the following line of code.

userService.getClass().getGenericSuperclass().getTypeName()
return:
com.gax.service.BaseService<com.gax.service.OrderService, com.gax.service.StockService>

5. Get the generic variables of BaseService, the parent class of UserService

for (TypeVariable<? extends Class<?>> typeParameter : userService.getClass().getSuperclass().getTypeParameters())
{
    System._out_.println(typeParameter.getName());
}

6. According to steps 4 and 5, we can know that the specific type corresponding to o is OrderService, and the specific type corresponding to s is StockService.

7. Call oField.getGenericType() to know which generic type is used by the current field and get the specific type

Usage of @Qualifier

Define two annotations:

@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier("random")
public @interface Random {
}

@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier("roundRobin")
public @interface RoundRobin {
}

Define an interface and two implementation classes to represent load balancing:

public interface LoadBalance
{
    String select();
}

@Component
@Random
public class RandomStrategy implements LoadBalance
{
    @Override
    public String select()
    {
        return null;
    }
}

@Component
@RoundRobin
public class RoundRobinStrategy implements LoadBalance
{
    @Override
    public String select()
    {
        return null;
    }
}

use:

@Component
public class UserService
{
    @Autowired
    @RoundRobin
    private LoadBalance loadBalance;

    public void test()
    {
        System.out.println(loadBalance);
    }
}