Spring Principles - Spring's most frequently used feature-dependent injection, the source of which must be known; this is the core of our daily development; understand the source; this is enough;

Posted by UseeHere on Mon, 20 Sep 2021 18:33:53 +0200

@TOC # Spring Series
Record every step in the program u auth:huf

Reply:

In the front of the article, I will reply to some students'questions. Note that if the return return value is not written when using InstantiationAwareBeanPostProcessor;This is in the process of creating objects;This will cause the @Autowired injection inside the object to fail;The object is incomplete;Note how InstantiationAwareBeanPostProcessor is written at this time;Because you are now involved in the instantiation of Bean;

Spring Dependent Injection

As a new chapter in Spring;Bean's life cycle has been described above;Now enter a new chapter, Spring Dependent Injection;

Let's start by asking three questions:

First question: How many ways can I rely on injection?

In an interview;This question is not frequently asked;But if the person asking this question can see that the interviewer definitely understands Spring;How do you answer this question to reflect your understanding of Spring?

There are two ways to rely on injection in Spring:

1 Manual Injection

Why do the talks start with manual injection?A long, long time ago;When we write injections in Spring again;We write injections in XML files again;I was developing in an enterprise that worked with the government.There are very large configuration files, a very large one with few thousand lines of configuration inside.The method used is XML injection.Manual injection;At that time, I also met the first mentor in my life ~and was biased by the female mentor.. Continue ~
1: injection via set;

<bean name="studentService" class="com.huf.service.impl.StudentServiceImpl">
	<property name="studentMapper" ref="studentMapper"/>
</bean>

2: Injection by construction method;

<bean name="studentService" class="com.huf.service.impl.StudentServiceImpl">
	<constructor‐arg index="0" ref="studentMapper"/>
</bean>

So there are two ways to inject manually: one is to inject through set mode;2. injection by means of construction;

2 Auto Injection

1: autowire auto-injection of XML;
There is a property in the bean tag of the XML;There are several parameters in autowire:
1). byType 2). byName 3). constructor 4). default 5). no

<bean name="studentService" class="com.huf.service.impl.StudentServiceImpl" autowire = "byType">
</bean>

In this case, Spring automatically assigns values to all properties in the userService (it is not necessary to have the @Autowired annotation on this property, but it needs a corresponding set method for this property)
Friends interested in these ways can try one by one.The article is longer;There's not much demonstration here;

Source code: Spring parses all methods of the current class while populating attributes during the Bean creation process;During the parsing process Spring gets a class called PropertyDescriptor, which is used to describe the get and set methods of a property.PropertyDescriptor is a class of java.bean s;If you have a favorite buddy, you can check the following by yourself;This type is very convenient;Here is a simple example of how I personally use this class:

By attribute name and class class Establish PropertyDescriptor  
PropertyDescriptor pd = new PropertyDescriptor(declaredField.getName(), clazz);
You can get it from this class Method Method; This is the reading method that is get
Method method = pd.getReadMethod();
Object invoke = method.invoke(obj);
String v = execute((String) invoke, maskStr, staNumber, endNumber);
This is the writing method that is set;
pd.getWriteMethod().invoke(obj, v);

A key

2:@Autowired annotation auto-injection;Autowired we focus on

Essentially, the @Autowired annotation provides the same 
capabilities as described in Autowiring Collaborators but with more 
fine‐grained control and wider applicability

First, let's see that @Autowired essentially provides the same functionality;But with finer-grained control and wider applicability;

@Autowired acts more granularity;

The autowire s in XML control all the properties of the whole bean, while the @Autowired annotation is written directly on a property, a set method, a construction method; @Autowired is the combination of ByType and ByName in Xml

	 Find first by attribute type Bean,If more than one is found, then one is determined by the attribute name
	 @Autowired
     private StudentMapper studentMapper;
     Find first by method parameter type Bean,If more than one is found, then one is determined by the parameter name
     @Autowired
     public void setStudentMapper (StudentMapper studentMapper){
     }
     Find first by method parameter type Bean,If more than one is found, then one is determined by the parameter name
     @Autowired
     public StudentServiceImpl(StudentMapper studentMapper) {
    }
  1. Attribute Injection
  2. set method injection
  3. Construction Method Injection

These are our theoretical points;

Second, how did @Autowired get in?

In the first question we talked about finding by type and then selecting by name.So how do I find it?Let's explore with questions

That's a simple injection.This class is instantiated and initialized through RootBeanDefinition and a very large number of BeanPostProcessor s during our Spring lifecycle.Instantiation is the creation of a Bean that is initialized after it is created.Complete the entire Bean and save it in the singleton pool. This is the code that will be injected after the Bean is created.

Interpolate your own views on the source code in the following source codes;The source code is Spring version 5.3.5;
Now we're doing Dependency Injection Code Tracking in Debug mode;
Previous Provinces... In AbstractBeanFactory in this class: the doGetBean method allows us to see the whole Bean creation process;In the previous section, we explained in the source that interesting calls to the previous section were written directly in the search for doCreateBean;

	protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
		Start instantiating Bean
		BeanWrapper instanceWrapper = null;
		judge Bean Is it singular if it is
		if (mbd.isSingleton()) {
			First from Factory delete Bean; Here's one ConcurrentMap That is, delete what is being created Bean Packaging object for;
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		Is it deleted? null;  Then start createBeanInstance
		if (instanceWrapper == null) {
			Start Entering createBeanInstance
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		The following source is temporarily unresolved;  More on in later chapters; That's it for now
-----------------------------------------------------------------------------
		Connect the previous source code here to show you all the source code for this method:
		
		Get instantiated Bean  At this time Bean The property inside is empty
		Object bean = instanceWrapper.getWrappedInstance();
		Get the one being created Bean class file; 
		Class<?> beanType = instanceWrapper.getWrappedClass();
		You don't have to say much here but you should be able to see it.;
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}
		Here's where we put ourselves back MergedBeanDefinitionPostProcessors Scheduling Method for Post Processor; 
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

		Cache singletons so that circular references can be resolved
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			This approach solves circular dependencies; Circular dependency will be addressed in the next chapter;
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		It's time to start Bean Property injection for;  
		Object exposedObject = bean;
		try {
			This method is the primary method; 
			populateBean(beanName, mbd, instanceWrapper);
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		All of the following sources are omitted;  Get into populateBean Go inside;
	}

Enter populateBean

	@SuppressWarnings("deprecation")
	protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
		Hide the preceding code first by convention; Readable;
		There's a preceding saying that we'll pick it up first PropertyDescriptor What does this do for you to see my case above;
		PropertyDescriptor[] filteredPds = null;
		if (hasInstAwareBpps) {
			if (pvs == null) {
				pvs = mbd.getPropertyValues();
			}
			Cycle Current Bean Of BeanPostProcessor BP 
			 At 5.3.5 There are six other versions of this version and the number of other versions may vary
			 The main one BP yes : AutowiredAnnotationBeanPostProcessor
			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;
			}
		}
		Hide back code

This for loop This List is printed for you

After passing this bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
The memory reference to the value saved here to BeanWrapper.getWrappedInstance() is the corresponding Bean in the singleton pool that needs to be injected.So inject him here;Then any Bean outside has value inside;

AutowiredAnnotationBeanPostProcessor

postProcessProperties

	@Override
	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {		
		Seeing the name of a thing one thinks of its function; You should know what's going on in this approach;	
		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
		try {
			Dependent Injection Core Method;
			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;
	}

inject

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
		Well-known source code? Inside InjectedElement Place is every property that you rely on for injection;
		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);
			}
		}
	}

Note when inject2 enters this inject;This is it;

@Override
		protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
			filed That's the property we're injecting;
			Field field = (Field) this.member;
			
			Object value;
			Check Cache
			if (this.cached) {
				try {
					value = resolvedCachedArgument(beanName, this.cachedFieldValue);
				}
				catch (NoSuchBeanDefinitionException ex) {
					value = resolveFieldValue(field, bean, beanName);
				}
			}
			else {
				Go without cache resolveFieldValue
				value = resolveFieldValue(field, bean, beanName);
			}
			if (value != null) {
				ReflectionUtils.makeAccessible(field);
				field.set(bean, value);
			}
		}

resolveFieldValue

Finally, this property is injected last in the above method;

@Nullable
		private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
			Watch your first after passing in required Is it false; 
			stay Autowired The one inside required Indicates whether injection is necessary;Default to true
			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 {
				Find through this step value 
				value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
			}
			------Omit some code
			Return to this value;
			return value;
		}
	}

summary

Find the injection point:

During the process of creating a Bean, Spring uses AutowiredAnnotationBeanPostProcessor's **postProcessMergedBeanDefinition()** to find the injection point and cache it. The process of finding the injection point is:

  1. Traverses through all attribute fields of the current class
  2. Check to see if any of the @Autowired, @Value, @Inject fields exist on the field, and if they exist, consider the field to be an injection point
  3. If the field is static, no injection occurs
  4. Gets the value of the required property in @Autowired
  5. Constructs field information into an AutowiredFieldElement object and adds it to the currElements collection as an injection point object.
  6. Traverse all methods of the current class
  7. Determine if the current Method is a bridging method and if the original method is found
  8. Check to see if any of the @Autowired, @Value, @Inject exist on the method and consider it an injection point
  9. If the method is static, no injection occurs
  10. Gets the value of the required property in @Autowired
  11. Constructs method information into an AutowiredMethodElement object and adds it to the currElements collection as an injection point object.
  12. After traversing the fields and methods of the current class, the parent class will be traversed until there is no parent class
  13. Finally, the currElements collection is encapsulated as an InjectionMetadata object, which is cached as the injection point collection object for the current Bean.

Why static fields or methods are not supported

We assume:
In attributes, our Bean is a prototype Bean, that is, multiple instances. What happens if it uses static s for its attributes?If you want to understand the problem, you will know why it is not supported.

The byte code file of a static-modified method produces two identical methods. One of the methods has a synthetic bridge and both have an @Autowired annotation, so this situation needs to be addressed in Spring. When traversing to the bridge method, the original method must be found

Injection point for injection

In the **postProcessProperties()** method of AutowiredAnnotationBeanPostProcessor, Spring iterates through the injection points found and injects them in turn.

Field Injection

  1. Traverse all AutowiredFieldElement objects
  2. Encapsulate the corresponding field as a DependencyDescriptor object
  3. Call the resolveDependency() method of BeanFactory, pass in the DependencyDescriptor object, do a dependency lookup, and find the Bean object that the current field matches.
  4. Encapsulate the DependencyDescriptor object and the found result object beanName into a ShortcutDependencyDescriptor object for caching. For example, if the current Bean is a prototype Bean, then the next time you create the Bean, you can take the cached result object beanName directly to the BeanFactory to go to the bean object without having to look up it again.
  5. Use reflection to assign result objects to fields.

Set Method Injection

  1. Traverse all AutowiredMethodElement objects
  2. Traverse encapsulates the parameters of the corresponding method, encapsulating each parameter as a MethodParameter object
  3. Encapsulate the MethodParameter object as a DependencyDescriptor object
  4. Call the resolveDependency() method of BeanFactory, pass in the DependencyDescriptor object, do a dependency lookup, and find the Bean object that the current method parameter matches.
  5. Encapsulate the DependencyDescriptor object and the found result object beanName into a ShortcutDependencyDescriptor object for caching. For example, if the current Bean is a prototype Bean, then the next time you create the Bean, you can take the cached result object beanName directly to the BeanFactory to go to the bean object without having to look up it again.
  6. Using reflection, all result objects found are passed to the current method and executed.

The focus of this chapter has been fully stated.If you want to hear the source of other notes, you can leave a message.If I have time, I will explain it to my classmates one by one.

SEE YOU

Topics: Java Spring Interview