Spring source code learning -- dependency injection source code analysis

resolveDependency() implementation

 

  The previous article analyzed the working principle and source code of byname (bytype) and @ Autowired annotation in Spring. The @ Autowired annotation depends on injection, in which the injection point injection, whether attribute injection or method injection, has the same method

org.springframework.beans.factory.support.DefaultListableBeanFactory# resolveDependency

@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 implementation of * * resolveDependency() * * method in DefaultListableBeanFactory:

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

		// Set parameterNameDiscoverer to get the name of the method input parameter
        // The default value of Bean factory is: private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
		descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
		// The required type is Optional
		if (Optional.class == descriptor.getDependencyType()) {
			return createOptionalDependency(descriptor, requestingBeanName);
		}
		// The required type is ObjectFactory, or ObjectProvider
		else if (ObjectFactory.class == descriptor.getDependencyType() ||
				ObjectProvider.class == descriptor.getDependencyType()) {
			return new DependencyObjectProvider(descriptor, requestingBeanName);
		}
		else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
			return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
		}
// This should be partially touched by us. In fact, no matter what method, it is finally handed over to the doResolveDependency method
		else {
			// If the @ Lazy annotation is used on the attribute or set method parameter, a proxy object will be constructed and returned. Only when the proxy object is actually used will the type filter Bean be performed
			Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
					descriptor, requestingBeanName);
			if (result == null) {
				// descriptor represents a property or a set method
				// requestingBeanName indicates the Bean that is undergoing dependency injection
				result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
			}
			return result;
		}
	}

Special processing is carried out for some special types, and general general processing will be called   doResolveDependency   method.

Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
					descriptor, requestingBeanName);

Judge whether the injected field or method parameter has @ Lazy annotation. Getautowirecandideresolver() gets contextannotationautowirecandideresolver. getLazyResolutionProxyIfNecessary method:

  If @ Lazy   Annotation injection, the returned is not a specific instance, but a proxy object, which is assigned to this attribute.

If it is a normal @ Autowired annotation, it will call   doResolveDependency   method.

  If the current descriptor has done dependency injection before, you can directly take the shortcut, which is equivalent to the cache. In short, you can go to the cache of the Bean factory to see if there is a Bean with this name. If there is, you can return it directly. There is no need to continue

Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);

@Processing of value annotation:

Getautowirecandideresolver() gets the contextannotationautowirecandideresolver and getSuggestedValue method, which is specifically implemented in the parent class qualifiernannotationautowirecandideresolver:

  Judge whether the injected field or method parameter has @ value annotation. If yes, get the value of @ value annotation. If not, return null

  Get the Value value of the @ Value annotation is not null. First fill in the placeholder (through the Environment), then parse the Spring expression (#{}), and return the result after type conversion if necessary

After that, the array, Collection, Map and other types are processed, and automatic injection is also supported

  Because it is an array or container, Sprng can directly inject all bean s matching the type into the array or container. The processing logic is:

1. Determine the component type of the container or array. if else should be treated separately

2. Call the findAutowireCandidates (core method) method to obtain the map (beanname - > bean instance) matching the component type

3. Add beanNames to autowiredBeanNames

If it is not a collection array type, continue:

Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);

Get all the [type] matching Beans and form a Map (Map is used here because there may be more than one meeting the conditions, key is beanName, value may be bean object or beanclass). This method is particularly important. The matching of generic types and the resolution of @ Qualifierd are all here. The details are broken down below

If there is no qualified bean and required is true, an exception is thrown

If multiple beans are found according to the type, further filter out one

  The determineAutowireCandidate method filters out a qualified Bean from multiple beans:

	@Nullable
	protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
		Class<?> requiredType = descriptor.getDependencyType();
		// candidates means to judge whether one of these beans is @ Primary based on multiple beans found by type
		String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
		if (primaryCandidate != null) {
			return primaryCandidate;
		}
		// Take the Bean with the highest priority
		String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
		if (priorityCandidate != null) {
			return priorityCandidate;
		}
		// Fallback
		// The name of the matching descriptor is either the name of the field or the name of the input parameter of the set method
		for (Map.Entry<String, Object> entry : candidates.entrySet()) {
			String candidateName = entry.getKey();
			Object beanInstance = entry.getValue();
			if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
					matchesBeanName(candidateName, descriptor.getDependencyName())) {
				return candidateName;
			}
		}
		return null;
	}

First, call the determinePrimaryCandidate method to judge whether one of the found bean s is marked with @ Primary annotation. Take a look at the determinePrimaryCandidate method:

	protected String determinePrimaryCandidate(Map<String, Object> candidates, Class<?> requiredType) {
		String primaryBeanName = null;
		for (Map.Entry<String, Object> entry : candidates.entrySet()) {
			String candidateBeanName = entry.getKey();
			Object beanInstance = entry.getValue();
			// isPrimary is to check whether the corresponding Bean definition information in the container (including the parent container) has @ Primary annotation
			if (isPrimary(candidateBeanName, beanInstance)) {
				if (primaryBeanName != null) {
					boolean candidateLocal = containsBeanDefinition(candidateBeanName);
					boolean primaryLocal = containsBeanDefinition(primaryBeanName);

					// This is equivalent to throwing an exception if an @ Primary has been found and another one has been found
					// @The Primary node can only be labeled on a Bean of the same type
					if (candidateLocal && primaryLocal) {
						throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
								"more than one 'primary' bean found among candidates: " + candidates.keySet());
					}
					else if (candidateLocal) {
						primaryBeanName = candidateBeanName;
					}
				}
				// Return the name of the found Bean marked with @ Primary
				else {
					primaryBeanName = candidateBeanName;
				}
			}
		}
		return primaryBeanName;
	}

If you find a Bean marked with @ Primary, return it directly. It can be seen that @ Primary has a very high priority

If not, take the bean with the highest priority and use the determinehyperstprioritycandidate method:

	// Determinehigh estprioritycandidate: select the highest priority Bean from a given Bean
	// What is the highest priority? Mainly for compatibility with the annotation javax.annotation.Priority provided by JDK6
	protected String determineHighestPriorityCandidate(Map<String, Object> candidates, Class<?> requiredType) {
		String highestPriorityBeanName = null;
		Integer highestPriority = null;
		for (Map.Entry<String, Object> entry : candidates.entrySet()) {
			String candidateBeanName = entry.getKey();
			Object beanInstance = entry.getValue();
			if (beanInstance != null) {
				//AnnotationAwareOrderComparator#getPriority
				// This is to be compatible with the javax.annotation.Priority annotation provided by JDK6, and then make a priority ranking
				// Note: This is not @ Order. It has nothing to do with it
				Integer candidatePriority = getPriority(beanInstance);
				if (candidatePriority != null) {
					if (highestPriorityBeanName != null) {
						// If the priority values are equal, it is not allowed. Attention should be paid here
						if (candidatePriority.equals(highestPriority)) {
							throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
									"Multiple beans found with the same priority ('" + highestPriority +
									"') among candidates: " + candidates.keySet());
						}
						// @The smaller the value set by Priority, the higher the Priority
						else if (candidatePriority < highestPriority) {
							highestPriorityBeanName = candidateBeanName;
							highestPriority = candidatePriority;
						}
					}
					else {
						highestPriorityBeanName = candidateBeanName;
						highestPriority = candidatePriority;
					}
				}
			}
		}
		return highestPriorityBeanName;
	}

If @ Primary and @ Priority do not exist, we will filter them according to beanName (I believe they will go here in most cases):

At this step, it is relatively simple. Matchsbeanname matches the key on the Map.
It should be noted that beans may have many aliases, so as long as one alias is the same, it is considered to be able to match   Please refer to AbstractBeanFactory#getAliases method for details

If you don't find it, you throw it wrong.
 

The above is to find multiple beans according to the type. If only one bean is found, take it out directly:

else {
	// We have exactly one match.
	Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
	autowiredBeanName = entry.getKey();
	instanceCandidate = entry.getValue();
}

The above operations get a qualified bean, and then there are some processing steps:

			// Put the autowiredBeanName found in it
			if (autowiredBeanNames != null) {
				autowiredBeanNames.add(autowiredBeanName);
			}
			// Because the bean we need to inject may not have been created, it may be BeanClass
			// The bottom layer is to call beanFactory.getBean(beanName); Ensure that the instance must have been instantiated
			if (instanceCandidate instanceof Class) {
				instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
			}
			Object result = instanceCandidate;
			// If the obtained bean object is a NullBean, judge required
			if (result instanceof NullBean) {
				if (isRequired(descriptor)) {
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				result = null;
			}
			// Verify again whether the type matches the type of result
			if (!ClassUtils.isAssignableValue(type, result)) {
				throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
			}
			return result;

The above is the analysis of * * resolveDependency() method in DefaultListableBeanFactory. The flow chart is as follows:

resolveDependency() flowchart

findAutowireCandidates() implementation

There is a method DefaultListableBeanFactory#findAutowireCandidates in resolveDependency: search the Map of beans with matching types, that is, find beans according to types:

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

String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
				this, requiredType, true, descriptor.isEager());

  Beannamesfortypeincludingagents: find the container first, then the parent container, and finally merge the results

org.springframework.beans.factory.support.DefaultListableBeanFactory#getBeanNamesForType

The doGetBeanNamesForType method will be called to find beanName according to the type. The general process is as follows:

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

// Match beans from resolvable dependencies according to type,
// Resolvable dependencies store = = 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;
		}
	}
}

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

//Not self referencing & & meets injection criteria
// Self reference judgment: the name of the found candidate bean is equal to the current bean name, or the current bean name is equal to the name of the factory bean
// isAutowireCandidate: this method is very important to determine whether the bean is allowed to be injected. Generic matching occurs in this method
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
	addCandidateEntry(result, candidate, descriptor, requiredType);
}

  If it is not itself, call the isAutowireCandidate method to determine whether the candidate can be used for automatic injection

org.springframework.beans.factory.support.DefaultListableBeanFactory#isAutowireCandidate:

	@Override
	public boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor)
			throws NoSuchBeanDefinitionException {
		//Getautowirecandidate resolver() is contextannotationautowirecandidate resolver
		return isAutowireCandidate(beanName, descriptor, getAutowireCandidateResolver());
	}



	protected boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor, AutowireCandidateResolver resolver)
			throws NoSuchBeanDefinitionException {

		String beanDefinitionName = BeanFactoryUtils.transformedBeanName(beanName);
		// If there is a Bean definition, go here (because some beans may come in directly from registerSingleton, but there is no Bean definition)  
		// Most of our injections go here
		if (containsBeanDefinition(beanDefinitionName)) {
			//The getMergedLocalBeanDefinition method is used to get the cached BeanDefinition object and merge its parent class and its own properties
			return isAutowireCandidate(beanName, getMergedLocalBeanDefinition(beanDefinitionName), descriptor, resolver);
		}
		// If an instance already exists, go here
		else if (containsSingleton(beanName)) {
			return isAutowireCandidate(beanName, new RootBeanDefinition(getType(beanName)), descriptor, resolver);
		}

		// The parent container may be null. If it is null, it must follow the else default value. true can be injected
		BeanFactory parent = getParentBeanFactory();
		if (parent instanceof DefaultListableBeanFactory) {
			// No bean definition found in this factory -> delegate to parent.
			return ((DefaultListableBeanFactory) parent).isAutowireCandidate(beanName, descriptor, resolver);
		}
		else if (parent instanceof ConfigurableListableBeanFactory) {
			// If no DefaultListableBeanFactory, can't pass the resolver along.
			return ((ConfigurableListableBeanFactory) parent).isAutowireCandidate(beanName, descriptor);
		}
		// The default value is true
		else {
			return true;
		}
	}


	protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd,
			DependencyDescriptor descriptor, AutowireCandidateResolver resolver) {

		String beanDefinitionName = BeanFactoryUtils.transformedBeanName(beanName);
			
		//The resolveBeanClass method mentioned earlier is mainly to ensure that this Class has been loaded
		resolveBeanClass(mbd, beanDefinitionName);
		//Whether a factory method name that references a non overloaded method has been specified. The default value is true
		if (mbd.isFactoryMethodUnique) {
			boolean resolve;
			synchronized (mbd.constructorArgumentLock) {
				resolve = (mbd.resolvedConstructorOrFactoryMethod == null);
			}
			// The factory method is mainly used here, which will be omitted first~
			if (resolve) {
				new ConstructorResolver(this).resolveFactoryMethodIfPossible(mbd);
			}
		}

		// Here comes the core. Contextannotationautowirecandidate resolver#isautowirecandidate method
		// The real implementation is in the parent class: qualifiernannotationautowirecandideresolver
		return resolver.isAutowireCandidate(
				new BeanDefinitionHolder(mbd, beanName, getAliases(beanDefinitionName)), descriptor);
	}

 QualifierAnnotationAutowireCandidateResolver#isAutowireCandidate

	@Override
	public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
		// Execute the upper layer judgment first. If the matching is successful, it will be matched by yourself
		boolean match = super.isAutowireCandidate(bdHolder, descriptor);
		if (match) {
			// Get all annotations on the attribute corresponding to the descriptor, check whether there is @ Qualifier, and if so, check whether it matches the current bdHolder
			match = checkQualifiers(bdHolder, descriptor.getAnnotations());
			if (match) {
				MethodParameter methodParam = descriptor.getMethodParameter();
				if (methodParam != null) {
					Method method = methodParam.getMethod();
					if (method == null || void.class == method.getReturnType()) {
						// @ Qualifier written before the method parameter
						match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
					}
				}
			}
		}
		return match;
	}

Execute the parent class first   isAutowireCandidate:

@Override
	public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
		if (!super.isAutowireCandidate(bdHolder, descriptor)) {
			// If explicitly false, do not proceed with any other checks...
			return false;
		}
		return checkGenericTypeMatch(bdHolder, descriptor);
	}

The parent class is still executed first   isAutowireCandidate:

	@Override
	public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
		return bdHolder.getBeanDefinition().isAutowireCandidate();
	}

First judge the autowireCandidate attribute in the BeanDefinition corresponding to beanName (indicating whether the bena is allowed to be injected into other beans, which is true by default)

autowireCandidate property in BeanDefinition:

  If false, it means that it cannot be used for automatic injection. If true, continue to judge

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

  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

Qualifiernannotationautowirecandideresolver #checkqualifiers: check whether the @ Qualifier annotation meets the conditions: @ Qualifier can be marked on the class or achieve the matching effect. (but it is not a bean name or an alias for a bean)

	/**
	 * Match the given qualifier annotations against the candidate bean definition.
	 * Match the given @ Qualifier annotation with the candidate bean definition ~ ~ ~ ~ (a simple book is to see if the type has been matched and @ Qualifier can still be matched)
	 */
	protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {
		// There are usually two annotations here, one @ Autowired and the other @ Qualifier  
		// Or there are other combined annotations~~~
		if (ObjectUtils.isEmpty(annotationsToSearch)) {
			return true;
		}
		SimpleTypeConverter typeConverter = new SimpleTypeConverter();
		for (Annotation annotation : annotationsToSearch) {
			Class<? extends Annotation> type = annotation.annotationType();
			boolean checkMeta = true;
			boolean fallbackToMeta = false;
			
			//isQualifier: judge whether the @ qualifier annotation and the 'javax.inject.Qualifier' annotation of JSR330 are also supported
			if (isQualifier(type)) {
				// This is the most important method of checkQualifier. It is an overloaded method... Its content is very long. I will analyze it here in the following steps:
				//1. bd.getQualifier see if tqualifiers have been defined in the Bean definition (but after my tracking, Bean defines this field: private final map < string, autowirecandidatequalifier > qualifiers; it will never be assigned. If anyone knows, please tell me what can be reserved by Spring)
				//2. Whether there is a specified Annotation on the attribute of AnnotatedElement qualifiedElement defined by the Bean. If so, take out the Annotation. Otherwise, continue to the next step
				//3. Is there this annotation on the resolvedFactoryMethod factory method? Otherwise, proceed to the next step (the next step is the key...)
				//4. Look for matching annotation on the target class Javadoc is also very clear, that is, go to the specific class to see if there are corresponding annotations, and take them out if there are.
				//(one detail): even if this class is proxied, you can still get the annotation marked on it, because: AnnotationUtils.getAnnotation(ClassUtils.getUserClass(bd.getBeanClass()), type)
				//5. Here, if the country obtains the corresponding @ Qualifier annotation, it will be compared. If the value value is the same, return true. Otherwise, go on
				//6. Next, get the attributes of this annotation, and then judge that if @ Qualifier has no value or an empty string, it will only return false, otherwise continue to look
				//7. Finally, it will be compared with the value value of the annotation above Bean (generally @ Component and other annotations) and the value value of @ Qualifier. If they are equal, it will eventually return true (please note: the alias alias of Bean will also return true if they are equal)
				//8. = = = = = in this way, we have completed the matching process between Bean definition and @ Qualifier======
				if (!checkQualifier(bdHolder, annotation, typeConverter)) {
					fallbackToMeta = true;
				}
				else {
					checkMeta = false;
				}
			}

			// This step is very effective: it is equivalent to supporting combined annotations. It parses even the annotation of the annotation
			// For example, we have @ Qualifier annotation on @ MyAnno, which will still be parsed here. There is a recursion inside
			if (checkMeta) {
				boolean foundMeta = false;
				for (Annotation metaAnn : type.getAnnotations()) {
					Class<? extends Annotation> metaType = metaAnn.annotationType();
					if (isQualifier(metaType)) {
						foundMeta = true;
						// Only accept fallback match if @Qualifier annotation has a value...
						// Otherwise it is just a marker for a custom qualifier annotation.
						if ((fallbackToMeta && StringUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) ||
								!checkQualifier(bdHolder, metaAnn, typeConverter)) {
							return false;
						}
					}
				}
				if (fallbackToMeta && !foundMeta) {
					return false;
				}
			}
		}
		return true;
	}

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

  4. If the result is empty after filtering, Spring will relax the filtering conditions and filter again

 

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.fztx.service.baseservice < com.fztx.service.orderservice, com. Fztx. 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 annotations are processed in org.springframework.context.annotation.CommonAnnotationBeanPostProcessor

Similar to @ AutoWired, the first step is to find the injection point

Find the injection point:

In the process of creating a Bean, Spring will use * * postProcessMergedBeanDefinition() * * of CommonAnnotationBeanPostProcessor to find the injection point and cache it. The execution time is that after the instantiation is completed and before the attribute assignment is performed, the post-processing of BeanDefinition (mergedbeandefinition postprocessor) will be executed

AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition

	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
		super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
		InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
		metadata.checkConfigMembers(beanDefinition);
	}

The findResourceMetadata method parses the injection point and puts it into the cache

buildResourceMetadata: 

  Injection at the injection point:
In the * * postProcessProperties() * * method of CommonAnnotationBeanPostProcessor, spring will traverse the found injection points and inject them in turn. After the instantiation of the time node (instantiawarebeanpostprocessor. Postprocessafterinstantiation) and after the dependency injection of spring is processed.

postProcessProperties() method of CommonAnnotationBeanPostProcessor

	@Override
	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
		InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
		try {
			metadata.inject(bean, beanName, pvs);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
		}
		return pvs;
	}

The first step is to find the injection point, but you can get it directly from the cache at this time

InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);


metadata.inject(bean, beanName, pvs) performs dependency injection on the injection point:

	public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
		Collection<InjectedElement> checkedElements = this.checkedElements;
		Collection<InjectedElement> elementsToIterate =
				(checkedElements != null ? checkedElements : this.injectedElements);
		if (!elementsToIterate.isEmpty()) {
			// Traverse each injection point for dependency injection
			for (InjectedElement element : elementsToIterate) {
				element.inject(target, beanName, pvs);
			}
		}
	}

Traverse each injection point for dependency injection. The element object we get here is a subclass ResourceElement of InjectedElement, but the ResourceElement does not override the inject method, so we still execute the InjectedElement's inject method:

		protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
				throws Throwable {

			if (this.isField) {
				Field field = (Field) this.member;
				ReflectionUtils.makeAccessible(field);
				field.set(target, getResourceToInject(target, requestingBeanName));
			}
			else {
				if (checkPropertySkipping(pvs)) {
					return;
				}
				try {
					Method method = (Method) this.member;
					ReflectionUtils.makeAccessible(method);
					method.invoke(target, getResourceToInject(target, requestingBeanName));
				}
				catch (InvocationTargetException ex) {
					throw ex.getTargetException();
				}
			}
		}

The getResourceToInject(target, requestingBeanName) method is to find the specific bean

Before looking at the getResourceToInject method, let's look at the construction method of ResourceElement:

		public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
			super(member, pd);
			Resource resource = ae.getAnnotation(Resource.class);
			String resourceName = resource.name();
			Class<?> resourceType = resource.type();

			// If you do not specify a specific name when using @ Resource, use the name of field or xxx in setXxx()
			this.isDefaultName = !StringUtils.hasLength(resourceName);
			if (this.isDefaultName) {
				resourceName = this.member.getName();
				if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
					resourceName = Introspector.decapitalize(resourceName.substring(3));
				}
			}
			// When using @ Resource, specify a specific name to fill in placeholders
			else if (embeddedValueResolver != null) {
				resourceName = embeddedValueResolver.resolveStringValue(resourceName);
			}

			// @In addition to specifying bean s, Resource can also specify type, which is Object by default
			if (Object.class != resourceType) {
				// If type is specified, verify whether the type of field or the first parameter type of set method matches the specified resourceType
				checkResourceType(resourceType);
			}
			else {
				// No resource type specified... check field/method.
				resourceType = getResourceType();
			}
			this.name = (resourceName != null ? resourceName : "");
			this.lookupType = resourceType;

			String lookupValue = resource.lookup();
			this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());

			Lazy lazy = ae.getAnnotation(Lazy.class);
			this.lazyLookup = (lazy != null && lazy.value());
		}

 

First, we get the two attributes of the @ Resource annotation, name and type

If no specific name is specified, use the name of the field or xxx in setXxx(), this.isDefaultName to record whether we have set the name. If a specific name is specified, fill in the placeholder to get the final resourceName, which is set to the name attribute of the ResourceElement,

If type is specified, verify whether the type of field or the first parameter type of set method matches the specified resourceType. If not specified, type is the type of attribute

Finally, it will judge whether it is marked by @ Lazy annotation

Next, look at the getResourceToInject(target, requestingBeanName) method:

@Override
		protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
			return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
					getResource(this, requestingBeanName));
		}

  If @ Lazy   Annotation injection, the returned is not a specific instance, but a proxy object, which is assigned to this attribute.

If not, call getResource(this, requestingBeanName):

 

// Assuming that no name is specified in @ Resource, and there is no corresponding bean in the name of field or xxx of setXxx(), find it from BeanFactory according to the field type or method parameter type
if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
	autowiredBeanNames = new LinkedHashSet<>();
	resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
	if (resource == null) {
		throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
	}
}
else {
	resource = beanFactory.resolveBeanByName(name, descriptor);
	autowiredBeanNames = Collections.singleton(name);
}

this.fallbackToDefaultTypeMatch: it can be understood as whether to find bean s according to types. The default is true

element.isDefaultName: @ Resource has a name specified, and it is not specified as true

factory.containsBean(name): judge whether the bean factory contains the bean with this name

Assuming that @ Resource does not specify a name, and there is no corresponding bean in the name of field or xxx (default name) of setXxx(), find it from BeanFactory according to the field type or method parameter type

resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);

This method is the method analyzed at the beginning of the above, and the bean is matched according to the type

Suppose @ Resource does not specify a name, but the name of field or xxx (default name) of setXxx() have corresponding beans in the bean factory; Or @ Resource specifies the name, so you can directly getBean according to the name

else {
	resource = beanFactory.resolveBeanByName(name, descriptor);
	autowiredBeanNames = Collections.singleton(name);
}

 resolveBeanByName:

 

@Resource annotation bottom workflow diagram:

@Resource assembly sequence
1. If both name and type are specified, find the unique matching bean from the Spring context for assembly. If not, throw an exception
2. If name is specified, the bean with matching name (id) will be found in the context for assembly. If it is not found, an exception will be thrown
3. If type is specified, the only bean matching the type will be found in the context for assembly. If it cannot be found or multiple beans are found, an exception will be thrown
4. If neither name nor type is specified, the assembly will be performed automatically by byName; If there is no match, it will be returned to an original type for matching. If it is matched, it will be assembled automatically;

Keywords: Java Spring source code

Added by decypher on Wed, 10 Nov 2021 05:57:56 +0200