Source code analysis of dependency injection in Spring

Posted by noobcody on Sat, 18 Sep 2021 03:10:59 +0200

In the previous section, we talked about the working principle and source code analysis of automatic injection (byName,byType) and @ Autowired annotation in Spring. Today, we will analyze 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 a dependency descriptor is passed in, and the method will find the corresponding unique Bean object from the BeanFactory according to the dependency description.

Let's analyze the specific implementation of * * resolveDependency() * * method in DefaultListableBeanFactory. The specific flow chart is as follows:
https://www.processon.com/view/link/5f8d3c895653bb06ef076688

findAutowireCandidates() implementation

Find the underlying process of beanName according to the type:
https://www.processon.com/view/link/6135bb430e3e7412ecd5d1f2

The corresponding execution flow chart is:
https://www.processon.com/view/link/5f8fdfa8e401fd06fd984f20 ​


1. Find out the names of all beans with type in BeanFactory. Note that it is the name, not the Bean object, because we can judge whether it matches the current type according to the BeanDefinition without generating a Bean object

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

3. Traverse the beanName found by type to determine whether the Bean corresponding to the current beanName can be automatically injected

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

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

6. If there is a @ Qualifier annotation on the current DependencyDescriptor, judge whether a Qualifier is defined on the current beanName, and whether it is equal to the Qualifier on the current DependencyDescriptor. If it is equal, it will match

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

Implementation of generic injection in dependency injection

First, in Java reflection, there is a Type interface, which represents types. The specific categories are:
1. raw types: that is, ordinary classes
2. parameterized types: corresponding ParameterizedType interface, generic type
3. array types: corresponds to GenericArrayType, generic array
4. Type variables: corresponds to the TypeVariable interface, representing type variables, that is, the defined generic types, such as T and K
5. primitive types: basic types, int and 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 the basic data type");
		} else {
			System.out.println(field.getName() + "Is 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() + "Is not a generic type");
		}

		if (field.getType().isArray()) {
			System.out.println(field.getName() + "Is a normal 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, but when the injection point is a generic type, it will also be processed, such as:

@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. When Spring scans, it is found that UserService is a Bean

2. Then take out the injection point, that 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 generic, so Spring needs to determine the specific types of O and s.

4. Because the Bean of UserService is being created, specific generic information can be obtained through userService.getClass().getGenericSuperclass().getTypeName(), such as com.zhouyu.service.baseservice < com.zhouyu.service.orderservice, com. Zhouyu. Service. Stockservice >

5. Then get the generic variable of the parent BaseService of UserService: for (TypeVariable <? Extends class <? > > typeparameter: UserService. Getclass(). Getsuperclass(). Gettypeparameters()) {system. Out. Println (typeparameter. Getname());}

6. From the above two codes, we can know that o corresponds to OrderService and s corresponds to StockService

7. Then call oField.getGenericType() to know which generic type the current field uses, and then you can 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 bottom workflow diagram:
https://www.processon.com/view/link/5f91275f07912906db381f6e

Topics: Java Spring