[Spring Topic] Spring's Bean Life Cycle Source Code Analysis - Phase 2 (2) (IOC Attribute Filling/Dependency Injection)

Table of contents

foreword

reading preparation

Since the Spring source code analysis is a process with a strong front-to-back relationship, and the analysis here is also explained in the order of the code, so if you do not understand the pre-knowledge, there is a high probability that you will not be able to understand the current content. Therefore, it is especially recommended to read my previous articles (from top to bottom):

(PS: Especially "Bean's Life Cycle Flowchart", to help you [open your eyes] and understand the process first. After all, [understanding the code through the business is much easier than understanding the business through the code]!!!!)
(PS: Especially "Bean's Life Cycle Flowchart", to help you [open your eyes] and understand the process first. After all, [understanding the code through the business is much easier than understanding the business through the code]!!!!)
(PS: Especially "Bean's Life Cycle Flowchart", to help you [open your eyes] and understand the process first. After all, [understanding the code through the business is much easier than understanding the business through the code]!!!!)

reading guide

We have already mentioned it in the last lesson. The general entry point of this Spring source code analysis is new AnnotationConfigApplicationContext("org.tuling.spring");, so I won’t repeat the explanation here. What I want to talk about in this lesson is the property filling/dependency injection of Spring IOC. Let’s give it directly to the entry here. The call chain is as follows: (the call chain is relatively deep, don’t get entangled in details)

  1. AbstractApplicationContext#refresh: Refresh method, don't care
  2. AbstractApplicationContext#finishBeanFactoryInitialization: All remaining (non lazy-init) singletons are instantiated here
  3. DefaultListableBeanFactory#preInstantiateSingletons: Instantiate all remaining (non lazy-init) singletons here (the above method, the core working method is here)
  4. DefaultListableBeanFactory#getBean: method to get Bean
  5. AbstractBeanFactory#doGetBean: returns an instance of the specified bean, which can be shared or independent
  6. AbstractBeanFactory#doGetBeanThe callback method written by a partial code in the above is as follows:
	// 如果是单例创建bean实例
	if (mbd.isSingleton()) {
    
    
		sharedInstance = getSingleton(beanName, () -> {
    
    
			try {
    
    
				return createBean(beanName, mbd, args);
			}
			catch (BeansException ex) {
    
    
				// Explicitly remove instance from singleton cache: It might have been put there
				// eagerly by the creation process, to allow for circular reference resolution.
				// Also remove any beans that received a temporary reference to the bean.
				destroySingleton(beanName);
				throw ex;
			}
		});
		beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
	}
  1. AbstractAutowireCapableBeanFactory#createBean: The central method of this class: create a bean instance, populate the bean instance, apply post-processors, etc.
  2. AbstractAutowireCapableBeanFactory#doCreateBean: [Instantiation] and the calling place of the subsequent statement cycle.
  3. [Entrance 1]: AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors: Apply merged BeanDefinition postprocessors to a given BeanDefinition.
  4. 【Entrance 2】AbstractAutowireCapableBeanFactory#populateBean: Populates a bean instance in the given BeanWrapper with property values ​​from the bean definition.

As shown in the above call chain, the last two methods are the core methods of our research. Why is it said that there are two entrances here? Mainly, [Property Filling/Dependency Injection] in this chapter will be divided into two parts for analysis. [Entrance 1] corresponds to [Part 1: Finding the injection point]; [Entrance 2] corresponds to [Part 2: Attribute filling and after filling]

reading advice

  1. Look at the source code, remember to get entangled in the details, otherwise it is easy to get stuck. Normally, just look at the main process
  2. If you don't understand, look at class annotations or method annotations. Spring is such an excellent source code, the annotations are really in place
  3. If you are an idea user, use the bookmark function of F11 more.
    • Ctrl + F11 Select files/folders, use mnemonics to set/cancel bookmarks (required)
    • Shift + F11 Pop-up bookmark display layer (required)
    • Ctrl +1,2,3...9 Locate to the bookmark position of the corresponding value (required)

Course content

1. Dependency injection method (pre-knowledge)

In Spring, there are two methods of attribute injection, namely: [manual injection] and [automatic injection].

1.1 Manual injection

When a Bean is defined in XML, it is manually injected, because the programmer manually assigns a value to an attribute. as follows:

<bean name="userService" class="com.luban.service.UserService">
 <property name="orderService" ref="orderService"/>
</bean>

Experienced students should know that the bottom layer above is setXxxinjected through methods. In addition, there is another way to inject through the construction method, as follows:

<bean name="userService" class="com.luban.service.UserService">
 <constructor-arg index="0" ref="orderService"/>
</bean>

So the bottom layer of manual injection is divided into two types: [set method injection] and [construct method injection].

1.2 Automatic injection

There are two types of automatic injection: [XML autowire automatic injection] and [@Autowired annotation automatic injection].

1.2.1 XML autowire automatic injection

In XML, we can specify the automatic injection mode of this bean when defining a bean, which has the following methods:

1.2.1.1 byType: inject by type

In the byType injection method, the bottom layer is setXxximplemented based on methods, so the setter method is indispensable. The type mentioned here is the type of [entry parameter].
Spring's process when passing byType's auto-fill properties is:

  1. Get the parameter type of the only parameter in the set method, and go to the container to get the bean according to the type
  2. If more than one is found, an error will be reported

An example of use is as follows:

<bean id="userService" class="com.luban.service.UserService" autowire="byType"/>
    public void setOrderService(OrderService orderService) {
    
    
        this.orderService = orderService;
    }

The type in the above example refers to orderServicethe type of the input parameterOrderService

1.2.1.2 byName: inject by name

In the byType injection method, the bottom layer is setXxximplemented based on methods, so the setter method is indispensable. The [name] mentioned here refers to setXxxthe latter Xxxpart.
Therefore, Spring's process when passing the autofill attribute of byName is:

  1. Find the name of the Xxx part corresponding to all set methods
  2. Get the bean according to the name of the Xxx part

An example of use is as follows:

    <bean id="userXmlBean" class="org.tuling.spring.xml.bean.UserXmlBean" autowire="byName"/>
    <bean id="walletXmlBean" class="org.tuling.spring.xml.bean.WalletXmlBean"/>

userXmlBeanAs above, the automatic injection type we defined is, and a bean byNamenamed is defined .walletXmlBean

public class UserXmlBean {
    
    

    private WalletXmlBean wallet;

    public void printProperty() {
    
    
        System.out.println(wallet);
    }

    public void setWalletXmlBean(WalletXmlBean param) {
    
    
        this.wallet = param;
    }
}

As above, we defined one UserXmlBean , which has member variables WalletXmlBean wallet. At the same time, a member method is declared for him printProperty()to print the address of its member properties.

Test code:

public class MyXmlApplicationContextTest {
    
    
    public static void main(String[] args) {
    
    
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        UserXmlBean userXmlBean = (UserXmlBean)context.getBean("userXmlBean");
        userXmlBean.printProperty();
    }
    
    // 系统输出:
    // org.tuling.spring.xml.bean.WalletXmlBean@1d16f93d
}

As above, there are two names for the attribute of the type, one is a member variable UserXmlBean, and the other is an input parameter of the setter method , but it does not prevent us from injecting at all. Because, according to the rules of , it is the latter part that is looked for. Not convinced enough, is it? Let's change the setter method inside, as follows:WalletXmlBeanwalletparambyNamebyNamesetXxxXxxUserXmlBean

    public void setWalletXmlBean123(WalletXmlBean param) {
    
    
        this.wallet = param;
    }

At this time, call again and output null.

1.2.1.3 constructor: inject according to the construction method

The constructor means injection through the construction method. In fact, this situation is relatively simple, not as complicated as byType and byName.
If it is a constructor, then you don’t need to write the set method. When a bean is injected through the construction method, spring uses the parameter information of the construction method to find the bean from the Spring container, and after finding the bean, pass it as a parameter to the construction method. In this way, a bean object is instantiated, and the attribute assignment is completed (the code for attribute assignment must be written by the programmer).
(PS: We will not consider the case where a class has multiple constructors here, and we will talk about inferring the constructor separately later. We only consider here that there is only one constructor with parameters.)
In fact, constructor injection is equivalent to byType+byName. When Spring passes the auto-fill property of byName, the flow is:

  1. Find the bean through the parameter type in the construction method. If there is only one bean for the corresponding type, that is it;
  2. If more than one is found, it will be determined according to the parameter name
  3. If it cannot be determined according to the parameter name in the end, an error will be reported

An example of use is as follows:

    <bean id="userXmlBean" class="org.tuling.spring.xml.bean.UserXmlBean" autowire="constructor"/>
    <bean id="walletXmlBean123" class="org.tuling.spring.xml.bean.WalletXmlBean"/>
    <bean id="walletXmlBean" class="org.tuling.spring.xml.bean.WalletXmlBean"/>

bean example:

public class UserXmlBean {
    
    

    private WalletXmlBean wallet;

    public void printProperty() {
    
    
        System.out.println(wallet);
    }
    
    public UserXmlBean(WalletXmlBean walletXmlBean) {
    
    
        this.wallet = walletXmlBean;
    }
}

The specific calling and error methods will not be introduced here, let’s go back and try it yourself

1.2.1.4 Others

Others, such as:

  • default: Indicates the default value, the autowire of a certain bean we have been demonstrating, and you can also set autowire directly in the <beans> tag. If it is set, then if the autowire set in the <bean> tag is default, then it will be used autowire set in the <beans> tag
  • no: means turn off autowire, no automatic injection
1.2.1.5 Summary of autowire automatic injection method of XML

Then the bottom layer of automatic injection of XML is actually:

  1. set method injection
  2. constructor injection

1.2.2 Automatic injection of @Autowired annotation

The @Autowired annotation is essentially a combination of byType and byName. It is first byType, if more than one is found, then byName. This is exactly the same as the injection principle of the xml construction method. that is:

  1. First find the bean according to the type, if there is only one bean of the corresponding type, that is it;
  2. If more than one is found, it will be determined according to the attribute name
  3. If it cannot be determined according to the attribute name in the end, an error will be reported

The @Autowired annotation can be written in:

  1. Attributes: first find the bean according to the attribute type, if more than one is found, then determine one according to the attribute name (attribute injection)
  2. On the construction method: first find the bean according to the method parameter type, if more than one is found, then determine one according to the parameter name (construction method injection)
  3. On the set method: first find the bean according to the method parameter type, if more than one is found, then determine one according to the parameter name (set method injection)

1.2.3 Automatic injection summary

It can be found that automatic injection in XML is quite powerful, so the question is, why do we usually use the @Autowired annotation? Instead of using the automatic injection method mentioned above?
In fact, the @Autowired annotation is equivalent to the replacement of the annotation method of the autowire attribute in XML. Essentially, the @Autowired annotation provides the same functionality as autowire, but withfiner-grained controland wider applicability.
The autowire in XML controls all the properties of the entire bean, while the @Autowired annotation is written directly on a property, a set method, or a construction method.
For another example, if a class has multiple constructors, then if you use XML's autowire=constructor, you can't control which constructor to use, and you can use the @Autowired annotation to directly specify which constructor you want to use.
At the same time, with the @Autowired annotation, you can also control which attributes you want to be automatically injected and which attributes you don't want. This is also a fine-grained control.

2. Dependency injection process

2.1 Brief review

The process of dependency injection can be roughly divided into the following three steps:[Find injection point], [Fill attribute], [After fill attribute]. But in fact, the process of [finding injection points] will be called in two places. The first one is the direction of the arrow, the place where [BeanDefinition post-processing] in the [Instantiation] stage. How do you understand it?Because, the implementation class of [Looking for injection point] is one of [BeanDefinition post-processing].
insert image description here
The concept mentioned above is somewhat convoluted. To put it simply, [Finding injection points] is to look for attributes, methods, etc. modified by @Autowird, @Value, @Inject, @Resourceannotations; then, when [Attribute filling], we will process these found injection points and set them to the corresponding Bean properties.

2.2 Concept review

In this [instantiation] process, some concepts of Spring’s underlying design were involved. I roughly introduced some explanations of Spring’s underlying concepts in the last note.
The main concepts involved are:

  • BeanDefinition (design drawing): BeanDefinition represents the Bean definition, and there are many attributes in BeanDefinition to describe the characteristics of a Bean
  • MergedBeanDefinitionPostProcessor: Merge BeanDefinition post processor. But here we are mainly talking about AutowiredAnnotationBeanPostProcessorfollowing CommonAnnotationBeanPostProcessor. What is the role of them? @AutowiredThe former is to deal with the automatic injection annotations defined inside Spring @Value; the latter is to deal with the annotations defined by jdk @Resource. MergedBeanDefinitionPostProcessorThis is mainly used postProcessMergedBeanDefinitionto complete the operation of [find injection point]
  • InstantiationAwareBeanPostProcessor: Bean post-processor aware of instantiation. This is the same as above, in fact, the main point is AutowiredAnnotationBeanPostProcessorto follow CommonAnnotationBeanPostProcessor. These two classes are also inherited , and the operation of the corresponding annotation [automatic injection] is completed InstantiationAwareBeanPostProcessorin itpostProcessProperties

CommonAnnotationBeanPostProcessorThe interface is defined as follows:

 /**
   * 这个后置处理器通过继承InitDestroyAnnotationBeanPostProcessor和InstantiationAwareBeanPostProcessor注解,
   * 获得了对@PostConstruct和@PreDestroy的支持。
   * 另外,这个类的核心处理元素是@Resource注解
   */
public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor
		implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {
    
    
	// 具体代码就不贴了。学到这里大家应该知道如何通过【接口】继承、实现来猜测类能力了吧
}

AutowiredAnnotationBeanPostProcessorThe interface is defined as follows:

// 继承类跟CommonAnnotationBeanPostProcessor 如出一辙,唯一不同的是,继承了功能更强大的
// SmartInstantiationAwareBeanPostProcessor(InstantiationAwareBeanPostProcessor子类)
// 实现这个类,是为了实现里面的推断构造方法
public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor,
		MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
    
    

2.3 Explanation of core methods

This section [Property Injection] will be divided into two parts. The first part is: [Find the injection point]; the rest is the second part.
Let me talk about the first part first, the first part mainly involves [3 classes, 7 core methods].
Part Two, TBD…

3. [Looking for injection point] method explanation

As I said above, [Looking for injection point] is actually called in two places. One is populateBean()[Merge BeanDefinition] before [Attribute Filling] applyMergedBeanDefinitionPostProcessors(), and the other is in [Attribute Filling].([Looking for the injection point] source code takes AutowiredAnnotationBeanPostProcessor as an example)

3.1 AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors: looking for the injection point code entry

Full path: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors
Apply MergedBeanDefinitionPostProcessors to specified bean definitions and call their postProcessMergedBeanDefinition method.

The source code is as follows:

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
    
    
	for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) {
    
    
		processor.postProcessMergedBeanDefinition(mbd, beanType, beanName);
	}
}

We click on the class object of this cycle and find their two implementation classes: AutowiredAnnotationBeanPostProcessorand CommonAnnotationBeanPostProcessor. For convenience, we only give an example here AutowiredAnnotationBeanPostProcessor, because the implementation methods of the two are basically the same, except that the Spring annotations processed by the former are written by Spring itself; the JDK annotations processed by the latter.

3.2 AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition

Method call chain: invoked by applyMergedBeanDefinitionPostProcessors() in 3.2
Full path: org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
method annotation: Post-process the given merged bean definition of the specified bean.

The source code is as follows:

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

findAutowiringMetadata()Interpretation of the method: the code is very simple, the real work in the whole process is actually the method inside

3.3 AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata: Find the injection point

Method call chain: called by applyMergedBeanDefinitionPostProcessors() of 3.2
Full path: org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata
method annotation: looking for injection point

**	private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
    
    

		// 设置缓存key
		String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
		
		// 先看缓存里面有没有
		InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
		if (InjectionMetadata.needsRefresh(metadata, clazz)) {
    
    
			synchronized (this.injectionMetadataCache) {
    
    
				metadata = this.injectionMetadataCache.get(cacheKey);
				if (InjectionMetadata.needsRefresh(metadata, clazz)) {
    
    
					if (metadata != null) {
    
    
						metadata.clear(pvs);
					}
					metadata = buildAutowiringMetadata(clazz);
					this.injectionMetadataCache.put(cacheKey, metadata);
				}
			}
		}
		return metadata;
	}**

Interpretation of the method: Here, a cache is used. To put it bluntly, it is a map to judge whether the [injection point has been found], and it is also for the convenience of subsequent injection. Here, the core operation is to buildAutowiringMetadataconstruct the injection point information of the current class by calling it, and wrap it up InjectionMetadata.

*3.4 AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata: build injection point

Method call chain: called by findAutowiringMetadata() of 3.3
Full path: org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata
method annotation: build injection point

The source code is as follows:

private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
    
    

	// 第一步:判断当前类是否候选类(是否需要【寻找注入点】)
	if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
    
    
		return InjectionMetadata.EMPTY;
	}

	List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
	Class<?> targetClass = clazz;

	do {
    
    
		final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
		
		// 第二步:利用反射,寻找【字段】上是否有【自动注入】的注解
		ReflectionUtils.doWithLocalFields(targetClass, field -> {
    
    
			MergedAnnotation<?> ann = findAutowiredAnnotation(field);
			if (ann != null) {
    
    
				if (Modifier.isStatic(field.getModifiers())) {
    
    
					if (logger.isInfoEnabled()) {
    
    
						logger.info("Autowired annotation is not supported on static fields: " + field);
					}
					return;
				}
				boolean required = determineRequiredStatus(ann);
				currElements.add(new AutowiredFieldElement(field, required));
			}
		});


		// 第三步:利用反射,寻找【方法】上是否有【自动注入】的注解
		ReflectionUtils.doWithLocalMethods(targetClass, method -> {
    
    
			Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
			if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
    
    
				return;
			}
			MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
			if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
    
    
				if (Modifier.isStatic(method.getModifiers())) {
    
    
					if (logger.isInfoEnabled()) {
    
    
						logger.info("Autowired annotation is not supported on static methods: " + method);
					}
					return;
				}
				if (method.getParameterCount() == 0) {
    
    
					if (logger.isInfoEnabled()) {
    
    
						logger.info("Autowired annotation should only be used on methods with parameters: " +
								method);
					}
				}
				boolean required = determineRequiredStatus(ann);
				PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
				currElements.add(new AutowiredMethodElement(method, required, pd));
			}
		});

		elements.addAll(0, currElements);
		targetClass = targetClass.getSuperclass();
	}
	while (targetClass != null && targetClass != Object.class);

	return InjectionMetadata.forElements(elements, clazz);
}

Interpretation of the method: The above method seems very long, but it is divided into three steps as a whole, and there is nothing particularly difficult to understand. Especially, if you have read my previous [Handwritten Spring-Guide Article].
The first step is to judge whether the current class is a candidate class (whether it is necessary to [find the injection point]). To be honest, I’m not sure what it means here, looking at the code is to filter java.the classes and annotations at the beginning (Baidu, the beginning of java. is generally the JDK open API, and the meta-annotations are declared inside, such as: @Retention, @Target, etc. ). So my understanding is that the judgment logic here is to allow Spring classes, or our custom classes, to use JDK's [automatic assembly] annotations, such as @Resource; but JDK can only use JDK's own [automatic assembly] annotation.
The second step: We all know that @Autowiredannotations can be modified on fields and methods. The second step is to process annotations of the [field] type.
The third step: process annotations of the [method] type.
The source code implementations of the second and third steps are actually the same, but the processing objects are different, so here we only talk about the logic of processing [fields]. That is as follows:

ReflectionUtils.doWithLocalFields(targetClass, field -> {
    
    
				MergedAnnotation<?> ann = findAutowiredAnnotation(field);
				if (ann != null) {
    
    
					if (Modifier.isStatic(field.getModifiers())) {
    
    
						if (logger.isInfoEnabled()) {
    
    
							logger.info("Autowired annotation is not supported on static fields: " + field);
						}
						return;
					}
					boolean required = determineRequiredStatus(ann);
					currElements.add(new AutowiredFieldElement(field, required));
				}
			});

But let me say something first. You may have noticed that the whole process is do-whiledone in a loop body, why? In fact, the loop here do-whileis to deal with the injection of [Bean with inheritance relationship].

3.5 ReflectionUtils#doWithLocalFields: use reflection to traverse the [field] on the class

Method call chain: called by buildAutowiringMetadata() of 3.4
Full path: org.springframework.util.ReflectionUtils#doWithLocalFields
Method annotation: Call the given callback for all locally declared fields in the given class.

The reflection tool method is implemented as follows:

	public static void doWithLocalFields(Class<?> clazz, FieldCallback fc) {
    
    
		for (Field field : getDeclaredFields(clazz)) {
    
    
			try {
    
    
				fc.doWith(field);
			}
			catch (IllegalAccessException ex) {
    
    
				throw new IllegalStateException("Not allowed to access field '" + field.getName() + "': " + ex);
			}
		}
	}

(PS: The above code, if you are not clear about the use of functional interfaces or lambda expressions, you may not understand it, so you have to review it quickly)

3.6 AutowiredAnnotationBeanPostProcessor#findAutowiredAnnotation: Find the autowired annotation on the field

Method call chain: called by doWithLocalFields() in 3.5
Full path: org.springframework.util.ReflectionUtils#doWithLocalFields
method annotation: Call the given callback for all locally declared fields in the given class.

Then, in the reflection [callback function], call to findAutowiredAnnotationdetermine whether the current field and method have [automatic assembly] annotations. as follows:

	@Nullable
	private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
    
    
		MergedAnnotations annotations = MergedAnnotations.from(ao);
		for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
    
    
			MergedAnnotation<?> annotation = annotations.get(type);
			if (annotation.isPresent()) {
    
    
				return annotation;
			}
		}
		return null;
	}

Careful friends may ask, this.autowiredAnnotationTypeswhat is the value? Yes, I know yes @Autowiredand @Valueannotations, but where is the value assigned? Ah, I won't talk about this at the moment, it will be told to you in the chapter on Spring container startup. But I can tell you first that this is AutowiredAnnotationBeanPostProcessorinitialized in the constructor. as follows:

	public AutowiredAnnotationBeanPostProcessor() {
    
    
		this.autowiredAnnotationTypes.add(Autowired.class);
		this.autowiredAnnotationTypes.add(Value.class);
		try {
    
    
			this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
					ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
			logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
		}
		catch (ClassNotFoundException ex) {
    
    
			// JSR-330 API not available - simply skip.
		}
	}

3.7 Modifier.isStatic

Method call chain: called by doWithLocalFields() in 3.5
Full path: java.lang.reflect.Modifier#isStatic
Method annotation: Call the given callback for all locally declared fields in the given class.

Then, if a field or method annotated with @Autowired and @Value is found, it will also be judged whether the field or method is staticmodified, that is, static. Static ones will not be processed. The source code is as follows:

if (Modifier.isStatic(field.getModifiers())) {
    
    
						if (logger.isInfoEnabled()) {
    
    
							logger.info("Autowired annotation is not supported on static fields: " + field);
						}
						return;
					}

if (Modifier.isStatic(method.getModifiers())) {
    
    
					if (logger.isInfoEnabled()) {
    
    
						logger.info("Autowired annotation is not supported on static methods: " + method);
					}
					return;
				}

Explain? The reason is very simple, you will know from the prototype Bean. We know that the static belongs to the class, not to the object, so if you have to deal with the static every time you inject it, wouldn't it be overwritten repeatedly? Example:

@Component
@Scope("prototype")
public class OrderService {
    
    
}

@Component
@Scope("prototype")
public class UserService  {
    
    

 @Autowired
 private static OrderService orderService;

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

}

Looking at the above code, both UserService and OrderService are prototype beans. Assuming that Spring supports automatic injection of static fields, then call it twice now

UserService userService1 = context.getBean("userService")
UserService userService2 = context.getBean("userService")

Q At this time, what is the orderService value of userService1? Or is it the value it injects itself? The answer is no, once userService2 is created, the value of the static orderService field is modified, resulting in a bug.

3.8 Remaining steps

In the remaining steps, three things are done, as follows:

  1. set @Autowired(required = false)this property
  2. Wrap the resulting build point intoInjectionMetadata.InjectedElement
  3. Encapsulate all the injection points obtained, InjectionMetadataand then cache them

4. [Looking for injection point] logic flow chart

insert image description here
Process description:

  1. Traverse all attribute fields Field of the current class
  2. Check whether there is any one of @Autowired, @Value, @Inject on the field, if it exists, the field is considered to be an injection point
  3. No injection if the field is static
  4. Get the value of the required attribute in @Autowired
  5. Construct the field information into an AutowiredFieldElement object, and add it to the currElements collection as an injection point object.
  6. Traversing all methods of the current class Method
  7. Determine whether the current Method is a bridge method, if the original method is found
  8. Check whether there is any one of @Autowired, @Value, @Inject on the method, if it exists, the method is considered to be an injection point
  9. If the method is static, no injection takes place
  10. Get the value of the required attribute in @Autowired
  11. Construct the method information into an AutowiredMethodElement object, and add it to the currElements collection as an injection point object.
  12. After traversing the fields and methods of the current class, it will traverse the parent class until there is no parent class.
  13. Finally, the currElements collection is encapsulated into an InjectionMetadata object, which is used as the injection point collection object for the current Bean and cached.

four point fivespecial statement

Brothers, let me declare in advance that the following content is personally very complicated, and it is the most complicated part so far since I read the Spring source code in order. However, because it is divided into two parts, there is still a sense of hierarchy, so if you have no desire to continue watching, please watch the rest next time. [/狗头][/狗头]
So, I thought, in the following explanation, write in another way.

  1. First of all, I will give the flowchart first, and write it in a tree structure
  2. Secondly, I will map to the tree structure in a [top-to-bottom, left-to-right] way according to the calling order of the source code
  3. Finally here, I feel that everyone needs to open the Spring source code, read the article and read the source code at the same time
  4. I will not explain all the source codes, but only click on some unfamiliar or key source codes

5. [Property Filling] Logic Flowchart

The overall source code logic flow chart is as follows: (I vomit blood as I drew)
insert image description here

The picture is not clear when clicked. I suggest you: right-click and create a new window to open. Then you can zoom in and watch

6. [Property Filling] Method Explanation

We have already said in [Reading Suggestions] that [Entrance 2] is AbstractAutowireCapableBeanFactory#populateBean. In this method, we are talking about this method in the first and second layers of the attribute flow chart above.

6.1 AbstractAutowireCapableBeanFactory#populateBean

Full path: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
Method Explanation: Use property values ​​from the bean definition to populate the bean instance in the given BeanWrapper.

Corresponding flow chart:
insert image description here

The source code is as follows: (marked steps 1, 2, and 3 are the research content of this chapter)

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    
    

        // 空判断,有属性,但是bean为空,则报错
        if (bw == null) {
    
    
            if (mbd.hasPropertyValues()) {
    
    
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
            }
            else {
    
    
                // Skip property population phase for null instance.
                return;
            }
        }

        // 这个在之前的【实例化阶段】的【实例化后】讲过了,不在本次研究范围内
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    
    
            for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
    
    
                if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
    
    
                    return;
                }
            }
        }


        // 下面才是我们本节课要研究的起点
        // 下面才是我们本节课要研究的起点
        // 下面才是我们本节课要研究的起点

        PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

        // 步骤一:
        // 处理beanDefinition的autowire属性。比如@Bean标签就可以设置这个属性;xml也可以设置这个属性
        int resolvedAutowireMode = mbd.getResolvedAutowireMode();
        if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
    
    
            MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
            // Add property values based on autowire by name if applicable.
            if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
    
    
                autowireByName(beanName, mbd, bw, newPvs);
            }
            // Add property values based on autowire by type if applicable.
            if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
    
    
                autowireByType(beanName, mbd, bw, newPvs);
            }
            pvs = newPvs;
        }

        // 步骤二:
        // 处理@Autowired、@Value、@Resource等【自动注入】的属性填充(注意,之前是寻找注入点,这里才是真正赋值的地方)
        boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
        boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
        PropertyDescriptor[] filteredPds = null;
        if (hasInstAwareBpps) {
    
    
            if (pvs == null) {
    
    
                pvs = mbd.getPropertyValues();
            }
            for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
    
    
                PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
                if (pvsToUse == null) {
    
    
                    if (filteredPds == null) {
    
    
                        filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                    }
                    pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                    if (pvsToUse == null) {
    
    
                        return;
                    }
                }
                pvs = pvsToUse;
            }
        }

        // 依赖检查,【细枝末节】,不看了
        if (needsDepCheck) {
    
    
            if (filteredPds == null) {
    
    
                filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
            }
            checkDependencies(beanName, mbd, filteredPds, pvs);
        }

        // 步骤三:
        // 处理BeanDefinition里面的propertyValues。比如:我们在操作beanDefinition的时候会修改;
        // 或者,在第一步处理@Bean的autowire属性的时候,实际上也是把结果跟旧有BeanDefinition的propertyValues合并。
        // 最后在这里处理注入操作
        if (pvs != null) {
    
    
            applyPropertyValues(beanName, mbd, bw, pvs);
        }
    }

Interpretation of the method: The method looks very long, but in fact the logic is relatively clear. It's just a pity that the code style has changed suddenly, and it is obviously from a different person than the previous research method. For example, in the past, populateBean()the source code of the [Processing after instantiation] stage in Germany will be encapsulated by a resolveAfterInstantiation()method; step 1 will also be written as a resolveAutowireMode()method, and the semantics will be clearer. Alas, writing like this later makes reading the source code somewhat uncomfortable.
Not much nonsense, let's analyze the process inside.

  • Step 1: Mainly deal with autowirethe attributes of the @Bean tag. In fact, strictly speaking, it is to deal with the properties under beanDefinition autowire. It is estimated that you have not used it very much, as follows:
@Bean(autowire = Autowire.BY_NAME)
public OrderService orderService1() {
    
    
    return new OrderService();
}

or this:

 <bean id="userService" class="org.example.spring.bean.UserService" autowire="byType"/>
  • Step 2: Process @Autowired, @Value, @Resource and other [automatic injection] attribute filling (note that before looking for the injection point, this is where the real value is assigned). This is the core content of this lesson, which will be explained in detail later.
  • Step 3: Process the propertyValues ​​in the BeanDefinition. For example: we will modify it when operating the beanDefinition; or, when processing the autowire attribute of @Bean in the first step, we actually merge the result with the propertyValues ​​of the old BeanDefinition, and finally process the injection operation here. There's really nothing to say about this

The following focuses on the attribute filling of step 2 [automatically inject annotations]

6.2 InstantiationAwareBeanPostProcessor#postProcessProperties: processing properties

As we mentioned in the previous [Concept Review], what is used here is actually the AutowiredAnnotationBeanPostProcessorsame CommonAnnotationBeanPostProcessor, so here is a simple AutowiredAnnotationBeanPostProcessorexample.

Method call chain: called by populateBean() in 6.1
Full path: org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties
method annotation: Before the factory applies the given property values ​​to the given bean, they are For post-processing, no attribute descriptors are required.

Corresponding flow chart:
insert image description here

The source code is as follows:

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

Interpretation of the method: Two things are done here, the first of which is the entrance of [Finding the injection point]. We have also introduced it before, and also said that [Find injection point] will be called in two places, and here is the second place. But here, the things in the cache are usually obtained directly, and there are not many cases of [finding the injection point] again, these belong to the [details], after all, whether we can see or not has little effect on our grasp of the overall context.
Therefore, the most important thing here is to look at this metadata.inject()method, and you will know the meaning of injection at a glance

6.3 InjectionMetadata#inject: inject according to the injection point

Method call chain: called by postProcessProperties() in 6.2
Full path: org.springframework.beans.factory.annotationInjectionMetadata#inject
method annotation: inject properties of the target class

The source code is as follows:

	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()) {
    
    
			for (InjectedElement element : elementsToIterate) {
    
    
				element.inject(target, beanName, pvs);
			}
		}
	}

Interpretation of the method: The method is quite simple. This this.injectedElementsis the injection point information that we cached in the [Find the injection point] stage. So do you still remember the injection point information here? Ha, it is the object encapsulated by field class and method class. Here is to traverse all the injection points, whether it is a method class or a field class, and then call the injection method of the other party in turn.
Having said that, in order to maintain [single responsibility], Spring has designed two classes for different injection objects. They are:

  • AutowiredFieldElement: Indicates injection information about the annotated [field]
  • AutowiredMethodElement: Represents injection information about the annotated [method]

The two of them are AutowiredAnnotationBeanPostProcessordefined inner classes
. As usual, these two are similar. Let's take AutowiredFieldElementit element.inject()as an example.

6.4 AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject: field class attribute injection [entry]

Method call chain: called by inject() in 6.3
Full path: org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
method annotation: it is simple, execute element injection logic

The source code is as follows:

		@Override
		protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    
    
			Field field = (Field) this.member;
			Object value;
			if (this.cached) {
    
    
				try {
    
    
					value = resolvedCachedArgument(beanName, this.cachedFieldValue);
				}
				catch (NoSuchBeanDefinitionException ex) {
    
    
					// Unexpected removal of target bean for cached argument -> re-resolve
					value = resolveFieldValue(field, bean, beanName);
				}
			}
			else {
    
    
				value = resolveFieldValue(field, bean, beanName);
			}
			if (value != null) {
    
    
				ReflectionUtils.makeAccessible(field);
				field.set(bean, value);
			}
		}

Method interpretation: Obviously, the first time we call, theoretically there is no cache, so we directly look at elsethe logic

6.5 AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue: resolve field attribute injection [entry]

Method call chain: called by inject() of 6.4
Full path: org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject

The source code is as follows:

	@Nullable
		private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
    
    
			
			// 步骤一:属性注入准备工作
			DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
			desc.setContainingClass(bean.getClass());
			Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
			Assert.state(beanFactory != null, "No BeanFactory available");
			TypeConverter typeConverter = beanFactory.getTypeConverter();
			Object value;
			try {
    
    
				
				// 步骤二:属性注入
				value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
			}
			catch (BeansException ex) {
    
    
				throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
			}
	
			// 步骤三:设置bean依赖信息
			synchronized (this) {
    
    
				if (!this.cached) {
    
    
					Object cachedFieldValue = null;
					if (value != null || this.required) {
    
    
						cachedFieldValue = desc;
						registerDependentBeans(beanName, autowiredBeanNames);
						if (autowiredBeanNames.size() == 1) {
    
    
							String autowiredBeanName = autowiredBeanNames.iterator().next();
							if (beanFactory.containsBean(autowiredBeanName) &&
									beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
    
    
								cachedFieldValue = new ShortcutDependencyDescriptor(
										desc, autowiredBeanName, field.getType());
							}
						}
					}
					this.cachedFieldValue = cachedFieldValue;
					this.cached = true;
				}
			}
			return value;
		}
	}

Interpretation of the method: Overall, it can be divided into three steps.
The first step is to prepare for attribute injection, such as the previously set requiredattributes, what type converters are there, and to ensure the existence of the bean factory, etc.; the
second step is the core, and the following is where the real attribute injection is processed;
the third The first step is to set the bean dependency information. what is this? To put it simply, in order to facilitate later maintenance, two maps have been added to record the interdependence between beans. The two maps are:

  • dependentBeanMap: Records which beans the bean depends on. Record with beanName
  • dependenciesForBeanMap: Records which beans are dependent on the bean. It is also recorded by beanName

6.6 DefaultListableBeanFactory#resolveDependency: resolve attribute injection [entry]

Method call chain: from resolveFieldValue() of 6.5
Full path: org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency
method annotation: resolve the specified dependencies according to the beans defined in this factory.

Corresponding flow chart:
insert image description here

The source code is as follows:

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

		descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
		if (Optional.class == descriptor.getDependencyType()) {
    
    
			return createOptionalDependency(descriptor, requestingBeanName);
		}
		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);
		}
		else {
    
    
			Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
					descriptor, requestingBeanName);
			if (result == null) {
    
    
				result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
			}
			return result;
		}
	}

Interpretation of the method: There are a lot of judgments at the beginning, judging how to inject this attribute and whether to inject it. I don’t really care about the first three if-elseif-elseif. Obviously, when we use Spring, we basically don’t use these types. Friends who are interested can go and see for themselves. Let's just look at the last one else.
Here is a detail, if you judge that there is a place that needs to be injected, @Lazythen directly return a [proxy object], and return it directly (the way of writing here is to judge to result==nulldetermine whether there is @Lazy). I don't know if anyone has thought about it, why use a proxy object? Ah, do you still remember the [Agent Paradigm] mentioned in the previous article?
insert image description here

// 代理对象
public class ProxyModel extends ProxyTarget {
    
    
    private ProxyTarget proxyTarget;

    public void setProxyTarget(ProxyTarget proxyTarget) {
    
    
        this.proxyTarget = proxyTarget;
    }

    @Override
    public void run() {
    
    
        System.out.println("我代理对象可以在这里做加强---1");
        super.run();
        System.out.println("我代理对象也可以在这里做加强---2");
    }
}

Just above this. Then let's recall the characteristics of lazy loading, isn't it just injected when it is used. Therefore, in the proxy mode, proxyTarge==nullit is enough to make a judgment in the calling method of each lazy-loaded attribute. That's why the proxy object is returned

6.7 DefaultListableBeanFactory#doResolveDependency: Solving attribute injection, where the real work is done

Method call chain: from resolveDependency() of 6.6
Full path: org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency
method comment: resolve attribute injection, where the real work

Corresponding flow chart:
insert image description here
The source code is a bit long here, so I won’t take screenshots. Speaking of the length of the method, I still want to complain because it is different from the previous code style, and the author is obviously lazy.

summarize

Guess you like

Origin blog.csdn.net/qq_32681589/article/details/132309278