Automatic assembly of Spring source

Introduction

Autowired annotations are often used to inject dependent bean s during Spring development, which is also one of the hot issues in interviews.Today let's delve into the underlying implementation of automatic injection.First, let's start with the previous example, as follows:

@RestController
public class TestController {
    @Autowired
    List<ICheckRuleService> checkRuleService;

    @RequestMapping("/test")
    public void test(){
        checkRuleService.forEach(x->x.valid());
    }
}

Start with Filling Bean s

How does Autowired achieve auto-injection? Today, let's take a look at the source code.When Spring creates the TestController Bean, AbstractBeanFactory#doGetBean is called (if you are not familiar with Spring's Bean Creation process, you can leave a message for me, and later consider whether to write an IOC series). DoGetBean calls the doCreateBean() method to create the Bean, and after creating the Bean, the Bean is populated

try {
    this.populateBean(beanName, mbd, instanceWrapper);
    exposedObject = this.initializeBean(beanName, exposedObject, mbd);
}

There's a piece of code in populateBean that looks like it handles Autowired, autowireByName and autowireByType

PropertyValues pvs = mbd.hasPropertyValues() ? mbd.getPropertyValues() : null;
if (mbd.getResolvedAutowireMode() == 1 || mbd.getResolvedAutowireMode() == 2) {
    MutablePropertyValues newPvs = new MutablePropertyValues((PropertyValues)pvs);
    if (mbd.getResolvedAutowireMode() == 1) {
       this.autowireByName(beanName, mbd, bw, newPvs);
    }

   if (mbd.getResolvedAutowireMode() == 2) {
       this.autowireByType(beanName, mbd, bw, newPvs);
   }

    pvs = newPvs;
}

Let's verify that we found through breakpoint debugging that we don't get into if, so automatic injection isn't done here.So what's the use here? Put it on first, and then come back.

Post processor property padding

So where exactly did it get in?Let's move on to see that there's a BeanPostProcessor logic underneath this code, and through the breakpoint we see that there's a postprocessor for AutowiredAnnotationBeanPostProcessor. When this BeanPostProcessor finishes executing the postProcessPropertyValues method, the checkRuleService property of the testController has a value indicating that the property value is injected into the affirmative and AutowiredAnnotaTionBeanPostProcessor, let's follow up

There are two main parts of logic that go into AutowiredAnnotationBeanPostProcessor's postProcessPropertyValues method

  • First you see a logic for findAutowiringMetadata that knows by method name that it is getting the injection meta information for the current bean

  • CallMetadata.injectInjection Properties

public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
    InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass(), pvs);

    try {
        metadata.inject(bean, beanName, pvs);
        return pvs;
    } catch (BeanCreationException var7) {
        throw var7;
    } catch (Throwable var8) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", var8);
    }
}

Let's start with Part One: findAutowiringMetadata

Let's go into findAutowiringMetadata and see its logic. First we fetch it from the injectionMetadata Cache cache. If we don't get the value, we call buildAutowiringMetadata to build InjectionMetadata and set it into the cache after successful construction.

private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
        String cacheKey = StringUtils.hasLength(beanName) ? beanName : clazz.getName();
        InjectionMetadata metadata = (InjectionMetadata)this.injectionMetadataCache.get(cacheKey);
        if (InjectionMetadata.needsRefresh(metadata, clazz)) {
            synchronized(this.injectionMetadataCache) {
                metadata = (InjectionMetadata)this.injectionMetadataCache.get(cacheKey);
                if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                    if (metadata != null) {
                        metadata.clear(pvs);
                    }

                    metadata = this.buildAutowiringMetadata(clazz);
                    this.injectionMetadataCache.put(cacheKey, metadata);
                }
            }
        }

        return metadata;
    }

Let's take a look at buildAutowiringMetadata and follow up with the following source code:

Field and Method are obtained from the lass reflection of the current Bean, then auto-injection annotations are obtained by adjusting the findAutowiredAnnotation method for Field and Method, respectively, and different types of InjectedElement s are built based on whether the annotation type is required or not.

  • AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement:

boolean required = this.determineRequiredStatus(ann);
currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement(field, required));
  • AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement:

boolean required = this.determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement(method, required, pd));

Supplement: From the AutowiredAnnotationBeanPostProcessor constructor, we know that auto-injection handles properties (Field) or methods (Method) labeled by @Autowired and @Value):

public AutowiredAnnotationBeanPostProcessor() {

        this.autowiredAnnotationTypes.add(Autowired.class);

        this.autowiredAnnotationTypes.add(Value.class);

    //......

At this point, the metadata information that needs to be injected is already built, and the next step is the injection section.Take a look at the second part of postProcessPropertyValues.

Look again at Part Two:metadata.inject

The metadata information that needs to be injected has been obtained earlier, followed by the implementation of the metadata inject, which is followed up by a for loop, which calls the inject method of element ation

if (!((Collection)elementsToIterate).isEmpty()) {
    for(Iterator var6 = ((Collection)elementsToIterate).iterator(); var6.hasNext(); element.inject(target, beanName, pvs)) {
        element = (InjectionMetadata.InjectedElement)var6.next();
        if (logger.isDebugEnabled()) {
            logger.debug("Processing injected element of bean '" + beanName + "': " + element);
        }
    }
}

We debugged the breakpoint and found that the true type of element is AutowiredAnnotatIonBeanPostProcessor.AutowiredFieldElementAnd the current element real type is TestController.checkRuleService A collection of.

We go into the AutowiredFieldElement#inject method and try to get the current FieldValue from the cache first. We can't get it, so we go to the else branch, which resolves the current FieldProperty Value from the beanFactory.

value = AutowiredAnnotationBeanPostProcessor.this.beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);

Continue to follow up and find the doResolveDependency method actually called

Get closer to the truth, don't worry, keep following up

Found a multipleBeans of type Object and the result returned the same Object. We have a strong guess that this Object is the List property that we need to inject. Follow up to verify:

Let's look at the source code for the Collection branch

else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
            elementType = descriptor.getResolvableType().asCollection().resolveGeneric(new int[0]);
            if (elementType == null) {
                return null;
            } else {
                Map<String, Object> matchingBeans = this.findAutowireCandidates(beanName, elementType, new DefaultListableBeanFactory.MultiElementDescriptor(descriptor));
                if (matchingBeans.isEmpty()) {
                    return null;
                } else {
                    if (autowiredBeanNames != null) {
                        autowiredBeanNames.addAll(matchingBeans.keySet());
                    }
​
                    TypeConverter converter = typeConverter != null ? typeConverter : this.getTypeConverter();
                    Object result = converter.convertIfNecessary(matchingBeans.values(), type);
                    if (this.getDependencyComparator() != null && result instanceof List) {
                        ((List)result).sort(this.adaptDependencyComparator(matchingBeans));
                    }
​
                    return result;
                }
            }
        }

It calls findAutowireCandidates to get beans, findAutowireCandidates gets the dependent BeanNames internally, and then calls beanFactory#getBean based on the beanName loop to get the beans that need to be injected

this.findAutowireCandidates(beanName,elementType,new DefaultListableBeanFactory.MultiElementDescriptor(descriptor))

The beanFactory#getBean method, which eventually calls AbstractBeanFactory#doGetBean, obtains the attribute beans that need to be assembled.

public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) throws BeansException {
        return beanFactory.getBean(beanName);
    }

When all loops are executed, a multipleBeans is obtained, verifying the previous guess.It's really hard. Set up the cache now_(

Final PassField.setSet the value of the obtained List property to the current bean with the following code:

if (value != null) {
    ReflectionUtils.makeAccessible(field);
    field.set(bean, value);
}

After executing the field's set method, look at the checkRuleService property again to see the value

If it's a Method injection, it's called through reflectionMethod.invokeSetting attributes into method parameters is roughly the same process.At this point, the Autowired assembly process is over.

When we talked about populateBean earlier, there was a judgment based on autowireMode whether property injection was performed. The autowireMode obtained at that time was ==0. When will autowireMode be of value and assembled according to autowireByName and autowireByType?

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

It's also very understandable, as we know from the source that mbd here is a Root BeanDefinition, which means hereMbd.getResolvedAutowireMode() Get the value by Bean Definition or PostProcessor, then set the AutowireModel property to have a value.When we look at the autowireByType source (AbstractAutowireCapableBeanFactory#autowireByType) here, we can see that the autowireByType also calls resolveDependency and goes on to find the doResolveDependency it actually callsMethod, and AutowiredAnnotationBeanPostProcessor is also an automatic injection through this method, the following processes are the same.

To conclude

1. When the beans are created, populateBean() is called to populate the beans. All BeanPostProcessors are fetched in the populateBean() method and the BeanPostProcessor#postProcessPropertyValues() is looped to set properties

2. There is an AutowiredAnnotationBeanPostProcessor, which builds injection metadata InjectionMetadata from Field and Method auto-injection annotations (@Autowired and @Value) by reflecting Field and Method based on Current Bean's lass

3. Call the inject() method that injects the metadata InjectionMetadata, and the assembly properties (there are two types: AutowiredFieldElement and AutoowiredMethodElement) are calledThis.beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter) Resolve dependent attribute values

4. resolveDependency eventually calls resolveMultipleBeans, which calls findAutowireCandidates in the branch to get an instance of the injected bean s, and calls back to AbstractBeanFactory#GetdoBean, depending on the type of the current injection properties, branching from Array, Collection, Map, respectively.

5. After getting all the property bean instances that need to be injected, the whole process of automatic injection is completed by setting reflection into the corresponding property or method.

Keywords: Spring Attribute

Added by Japher on Mon, 08 Jun 2020 03:25:54 +0300