Spring-Spring dependency injection source code analysis (Part 2)

The working principle and source code analysis of the Automatic Injection (byName, byType) and @Autowired annotations in Spring before. In today’s lesson, let’s analyze what we haven’t finished yet. , the remaining core methods:

@Nullable
Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
  @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException;

This method means that when a dependency description (DependencyDescriptor) is passed in, the method will find the corresponding only Bean object from the BeanFactory based on the dependency description.

Let’s analyze the specific implementation of the **resolveDependency()** method in DefaultListableBeanFactory, specific flow chart:
The process of finding Beans based on Type in Spring | ProcessOn free online drawing, online flow chart, online mind map

findAutowireCandidates() implementation

The underlying process of finding beanName based on type: The underlying process of finding beanName based on type | ProcessOn free online drawing, online flow chart, online mind map
The corresponding execution flow chart is: dependency injection process | ProcessOn free online drawing, online flow chart, online mind map
?

  1. Find the names of all beans of type type in the BeanFactory. Note that it is the name, not the Bean object, because we can judge whether it matches the current type based on the BeanDefinition 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 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.

About the 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: corresponds to ParameterizedType interface, generic type
  3. array types: corresponding 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

You can take a good look at the results printed by the following code:
?

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");
  }

 }

}

In Spring, when the injection point is a generic type, it will also be processed, for example:
?

@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. Then take out the injection point, which is the two attributes o and s in BaseService.
  3. Next, you need to inject according to the injection point type, but o and s are both generic, so Spring needs to determine the specific types of o and s.
  4. Because the Bean of UserService is currently being created, you can obtain specific generic information through userService.getClass().getGenericSuperclass().getTypeName(), such as com.zhouyu.service .BaseService
  5. Then get the generic variable of BaseService, the parent class of UserService: for (TypeVariable> typeParameter: userService.getClass().getSuperclass().getTypeParameters()) { System._out_.println (typeParameter.getName()); }
  6. Through the above two pieces of code, we can know that the specific type corresponding to o is OrderService, and the specific type corresponding to s is StockService.
  7. Then call oField.getGenericType() to know which generic type is used by the current field, and to know the specific type.

?

?

?

Use 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);
 }

}

?

@Resource

@Resource annotation underlying workflow diagram:
@Resource annotation underlying working principle | ProcessOn free online drawing, online flow chart, online mind map

The knowledge points of the article match the official knowledge files, and you can further learn related knowledge. Java Skill TreeHomepageOverview 139324 people are learning the system