How about the automatic injection process of spring @ Autowired annotation?

Time:2020-11-27

During the interview, the interviewer asked, “what’s the job of spring annotation?”? “At present, I’m surprised. After all, does this not touch my misunderstanding of knowledge? Fortunately, I was quick witted and came up with a reply: I haven’t seen the workflow of spring annotation, but I know@AutowiredNotes of the workflow, not to mention a dunbala, the interviewer has repeatedly nodded.

In the interview, you should use the transfer topic flexibly, avoid answering “don’t know”, guide the interviewer to fall into the skills you are good at, and then have the opportunity to teach him to be a person.

@Autowired related classes

@The main function of Autowired annotation is to complete automatic injection, and it is very simple to use (spring has arranged it). However, if you want to know the internal reality of @ Autowired annotation, you need to take a look at the spring source code. Next, dissect the implementation principle of @ Autowired step by step. First, sort out the classes related to @ Autowired annotation, and then track the source code step by step until you understand the principle of @ Autowired.

Autowireedannotationbeanpostprocessor class

AutowiredAnnotationBeanPostProcessorThe main functions of @ wi are as follows:

How about the automatic injection process of spring @ Autowired annotation?

From the above figure, we can see that the top layer of Autowired annotation bean post processor is BeanPostProcessorIt’s a post processor. Of course, in addition to the post processor, there are also some in the middleInstantiationAwareBeanPostProcessorandMergedBeanDefinitionPostProcessor

Instantiationawarebeanpostprocessor interface

Postprocessbeforeinstance method

Invoking before Bean instantiation, you can return a Bean instance and return by default.null

@Nullabledefault Object postProcessBeforeInstantiation(Class<?> beanClass, String 
beanName) throws BeansException {      return null;}

Postprocessafterinstance method

After Bean is created, it is called before setting properties.

default boolean postProcessAfterInstantiation(Object bean, String 
beanName) throws BeansException {      return true;}

Postprocessproperties method

@Nullable default PropertyValues postProcessProperties(PropertyValues pvs, Object 
bean, String beanName)            throws BeansException {      return null;}

After Bean is created, it is called before setting properties.

Remember the instantiationawarebeanpostprocessor interface first, and then track where it is called, which is easy to understand.

MergedBeanDefinitionPostProcessor

MergedBeanDefinitionPostProcessorIt’s also an inheritanceBeanPostProcessorThe main function of the interface is to process operationsBeanDefinitionObject. Since bean instantiation is through beandefinition, some changes can be made during bean instantiation by operating beandefinition.

The mergedbeandefinitionpostprocessor has only two methods

Postprocessmergedbeandefinition method

void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, 
Class<?> beanType, String beanName);

The BeanDefinition of Bean is called after merging.

resetBeanDefinition

default void resetBeanDefinition(String beanName) {}

When a BeanDefinition is reset, it is called.

Autowirecapablebeanfactory interface

AutowireCapableBeanFactoryInherited fromBeanFactoryIn addition to providing basic bean operations, it can be inferred from the name of the interface as wellAutomatic injectionAbility. Autowirecapablebeanfactory provides four injection models:

  • AUTOWIRE_ No: there is no defined injection model shown
  • AUTOWIRE_ BY_ Name bean injection
  • AUTOWIRE_ BY_ Type: type injection through bean
  • AUTOWIRE_ Constructor: injected through bean’s construction method

The autowirecapablebeanfactory interface has many methods, but most of them are related to automatic injection. @The main function of Autowired is to set properties for a bean after it is instantiated. Therefore, in the autowirecapablebeanfactory interface, there is a createBeanMethod to create a bean and set its properties:

<T> T createBean(Class<T> beanClass) throws BeansException;

createBeanMethod, which is called when the bean is created. We’ll talk about it later.

AbstractAutowireCapableBeanFactory

How about the automatic injection process of spring @ Autowired annotation?

Abstractautowirecapablebeanfactory inheritanceAbstractBeanFactoryAnd realizedAutowireCapableBeanFactoryInterface, so it also implementsAutowireCapableBeanFactoryMediumcreateBeanmethod.

public <T> T createBean(Class<T> beanClass) throws BeansException {     

// Use prototype bean definition, to avoid registering bean as dependent bean.      

RootBeanDefinition bd = new RootBeanDefinition(beanClass);      
bd.setScope(SCOPE_PROTOTYPE);     

bd.allowCaching = ClassUtils.isCacheSafe(beanClass, getBeanClassLoader());      

return (T) createBean(beanClass.getName(), bd, null);}

The lifecycle of bean creation

By understanding the life cycle of bean creation, you can string the classes related to @ Autowired. First of all, we will not introduce the details of bean creation too much, only focus on automatic injection of relevant code.

Bean creation process

The default beans in spring are lazy loaded, so the creation of a bean will be called fromgetBeanIf you do not consider the method of creating the upper level container, you will not consider the following methods:

  • GetBean: method of beanfactory to obtain bean instance
  • Dogetbean: get bean instances in the following order: single instance pool and parent container. If the bean instance is not obtained from the above two ways, a new one will be created
  • Create bean: to create a bean, the createbean here is the same as the one described above
  • Do create bean: create bean instance
  • Populatebean: set the bean property

In the above processgetBeananddoGetBeanI don’t want to explain it, but I’ll focus on itcreateBeanAs mentioned earlierAbstractAutowireCapableBeanFactory.createBeanMethod, so you are callinggetBeanMethod to get an instance of a bean, if the bean instance has not been created, thencreateBeanWill be called.

By simply explaining the life cycle of bean creation, you can find the entry of @ Autowired annotation implementation, and then continue to tracecreateBeanmethod.

Collect injection meta information

The step of collecting and injecting meta information is actually callingAutowiredAnnotationBeanPostProcessorClass method.

Before bean creation

Here arecreateBeanMethod is called before Bean is created.postProcessBeforeInstantiationThe place. For the convenience of reading, some codes are omitted. The general process is as follows:

  • Call firstresolveBeforeInstantiationMethod, executionInstantiationAwareBeanPostProcessor.postProcessBeforeInstantiationmethod
  • IfpostProcessBeforeInstantiationIf the bean instance is returned, this instance will be returned directly. If nul is returned, the call will continuedoCreateBean
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {
 
   ...

   try {
      // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
      Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
      if (bean != null) {
         return bean;
      }
   }
   catch (Throwable ex) {
        ...
   }
   
     ...
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    ...
   
   ...
 }
   
 @Nullable
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
   Object bean = null;
   if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
      // Make sure bean class is actually resolved at this point.
      if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
         Class<?> targetType = determineTargetType(beanName, mbd);
         if (targetType != null) {
            bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
            if (bean != null) {
               bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
            }
         }
      }
      mbd.beforeInstantiationResolved = (bean != null);
   }
   return bean;
}

Here, the method of postprocessbeforeinstance of Autowired annotation beanpostprocessor will be called. Since Autowired annotation beanpostprocessor does not override this method, it does nothing.

Operation beandefinition

As mentioned above, if the postprocessbeforeinstance method returns null, it will continue to executedoCreateBeanmethod:

 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {


   // Allow post-processors to modify the merged bean definition.
   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;
      }
   }

  ...
 populateBean(beanName, mbd, instanceWrapper);
 ...
 

In the docreatebean method, theapplyMergedBeanDefinitionPostProcessorsmethod:

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
   for (BeanPostProcessor bp : getBeanPostProcessors()) {
      if (bp instanceof MergedBeanDefinitionPostProcessor) {
         MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
         bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
      }
   }

MergedBeanDefinitionPostProcessorInterface mentioned above, the Autowired annotation beanpostprocessor implements this interface, so it directly enters thepostProcessMergedBeanDefinitionmethod:


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

And then continue into thefindAutowiringMetadata, findautowiring metadata will callbuildAutowiringMetadataMethod to create the injection metadata, and then cache the metadata into theinjectionMetadataCacheAttribute:


private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
   // Fall back to class name as cache key, for backwards compatibility with custom callers.
   String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
   // Quick check on the concurrent map first, with minimal locking.
   InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
   if (InjectionMetadata.needsRefresh(metadata, clazz)) {
      synchronized (this.injectionMetadataCache) {
         metadata = this.injectionMetadataCache.get(cacheKey);
         if (InjectionMetadata.needsRefresh(metadata, clazz)) {
            ...
            metadata = buildAutowiringMetadata(clazz);
            this.injectionMetadataCache.put(cacheKey, metadata);
         }
      }
   }
   return metadata;
}
Create injection metadata

go throughbuildAutowiringMetadataMethod, which reflects the methods and properties of a class, looks up the parent class, and generatesInjectionMetadata

private InjectionMetadata buildAutowiringMetadata(final 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);
    }

Summary

To collect the injection metadata process, first callAutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinitionMethod, then callfindAutowiringMetadataMethod to find the metadata. If the injected metadata of the corresponding class is found, it will be calledbuildAutowiringMetadataMethod creationInjectionMetadataFinally, save the newly created injection metadata in theinjectionMetadataCacheCache it.

Set bean property

After receiving the metadata injection, the bean’s attributes are still not injected, and the execution attribute injection is required. Is it still theredoCreateBeanMethod, after collecting the injected metadata, thepopulateBean

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
   
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
   if (pvs == null) {
      pvs = mbd.getPropertyValues();
   }
   for (BeanPostProcessor bp : getBeanPostProcessors()) {
      if (bp instanceof InstantiationAwareBeanPostProcessor) {
         InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
         PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
         if (pvsToUse == null) {
            if (filteredPds == null) {
               filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
            }
            pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
               return;
            }
         }
         pvs = pvsToUse;
      }
   }
   }
}

You can see in thepopulateBeanCall theInstantiationAwareBeanPostProcessor.postProcessPropertiesSince we already know that Autowired annotation beanpostprocessor implements instantiationawarebanpostprocessor, you can directly view the implementation method:

@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;
}

postProcessPropertiesIt is very simple to find InjectMetadata and call it.InjectMetadata.injectmethod. I already know it here@AutowireAccording to the implementation mechanism ofInjectionMetadataThe information implemented in the.

If there is a need for in-depth study, those who are interested can continue to look down.

summary

This paper describes the @ autowire related classes and implementation mechanism. The implementation of @ autowire annotation is mainly to understandAutowiredAnnotationBeanPostProcessorClass, as well as collecting injection metadata and setting the call timing of injection properties.

By viewingAutowiredAnnotationBeanPostProcessorClass source code, I believe you can also customize the injection function.

My knowledge level is limited, if there are mistakes, thank you for your correction.

Welcome to my official account: Architecture digest, get exclusive learning resources for 120G, help your architect learn!

Official account back office replyarch028Access to information:

How about the automatic injection process of spring @ Autowired annotation?