SpringBoot boot source code parsing – AutoConfigure implementation principle SpringBoot source code parsing – @aliasfor annotation annotation SpringBoot source code parsing – SpringBoot boot boot boot process @ComponentScan implementation principle SpringBoot source parsing – @value, @autoWired implementation principle SpringBoot source parsing – Tomcat, SpringMVC starts SpringBoot source parsing — Logging, Environment starts

The @ComponentScan article explained how Spring scans the @Component annotated Bean, but how does the scanned Bean inject properties? As we all know, this function is mainly completed by using @Value and @Autowired annotations. This article mainly analyzes the implementation principle of @Value and @Autowired in Spring. Source code analysis based on Spring Boot 2.1

Resolution in front of the Spring injection process attribute’s article, said AbstractAutowireCapableBeanFactory# populateBean method is responsible for the injection of the bean attribute actually @ the Value, the @autowired annotation is also in the methods of processing.

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { ... PropertyDescriptor[] filteredPds = null; if (hasInstAwareBpps) { if (pvs == null) { pvs = mbd.getPropertyValues(); } for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; // #1 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; }}}... }Copy the code

# 1 call InstantiationAwareBeanPostProcessor# postProcessProperties extension methods, processing @ Value here, the @autowired annotation, etc.

In analytical SpringBoot startup process, said AutowiredAnnotationBeanPostProcessor handle @ Value, the @autowired annotation, etc. (AutowiredAnnotationBeanPostProcessor InstantiationAwareBeanPostProcessor implementation class) AutowiredAnnotationBeanPostProcessor#postProcessProperties

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { // #1 InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { // #2 metadata.inject(bean, beanName, pvs); }... return pvs; }Copy the code

Get metadata for Class annotations related to attribute injection

AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata -> buildAutowiringMetadata

private InjectionMetadata buildAutowiringMetadata(final Class<? > clazz) { LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();  Class<? > targetClass = clazz; do { final LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<InjectionMetadata.InjectedElement>(); ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() { @Override public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { // #1 AnnotationAttributes ann = findAutowiredAnnotation(field); if (ann ! = null) { if (Modifier.isStatic(field.getModifiers())) { if (logger.isWarnEnabled()) { logger.warn("Autowired annotation  is not supported on static fields: " + field); } return; } boolean required = determineRequiredStatus(ann); currElements.add(new AutowiredFieldElement(field, required)); }}}); . // #2 elements.addAll(0, currElements); targetClass = targetClass.getSuperclass(); } while (targetClass ! = null && targetClass ! = Object.class); return new InjectionMetadata(clazz, elements); }Copy the code

#1 find if field has @autowired, @value etc. #2 find if method has @autowired, @value etc

Injecmetadata #inject iterates all InjectedElement, calls AutowiredMethodElement, AutowiredFieldElement inject method, AutowiredFieldElement#inject InjectionMetadata#inject -> AutowiredFieldElement#inject

protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { Field field = (Field) this.member; Object value; if (this.cached) { value = resolvedCachedArgument(beanName, this.cachedFieldValue); } else { DependencyDescriptor desc = new DependencyDescriptor(field, this.required); desc.setContainingClass(bean.getClass()); Set<String> autowiredBeanNames = new LinkedHashSet<String>(1); TypeConverter typeConverter = beanFactory.getTypeConverter(); try { // #1 value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); } catch (BeansException ex) { throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex); }... } if (value ! = null) { // #2 ReflectionUtils.makeAccessible(field); field.set(bean, value); }}Copy the code

#1 Based on the annotation metadata, parse the property value #2 and inject the parsed result value into the bean

DefaultListableBeanFactory#resolveDependency -> DefaultListableBeanFactory#doResolveDependency

public Object doResolveDependency(DependencyDescriptor descriptor, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException { InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor); try { Object shortcut = descriptor.resolveShortcut(this); if (shortcut ! = null) { return shortcut; } Class<? > type = descriptor.getDependencyType(); // #1 Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); if (value ! = null) { if (value instanceof String) { // #2 String strVal = resolveEmbeddedValue((String) value); BeanDefinition bd = (beanName ! = null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); value = evaluateBeanDefinitionString(strVal, bd); } //#3 TypeConverter converter = (typeConverter ! = null ? typeConverter : getTypeConverter()); return (descriptor.getField() ! = null ? converter.convertIfNecessary(value, type, descriptor.getField()) : converter.convertIfNecessary(value, type, descriptor.getMethodParameter())); } // #4 Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter); if (multipleBeans ! = null) { return multipleBeans; } // #5 Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); if (matchingBeans.isEmpty()) { if (isRequired(descriptor)) { raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } return null; } String autowiredBeanName; Object instanceCandidate; // #6 if (matchingBeans.size() > 1) { autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); if (autowiredBeanName == null) { if (isRequired(descriptor) || ! indicatesMultipleBeans(type)) { return descriptor.resolveNotUnique(type, matchingBeans); } else { return null; } } instanceCandidate = matchingBeans.get(autowiredBeanName); } else { Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next(); autowiredBeanName = entry.getKey(); instanceCandidate = entry.getValue(); } if (autowiredBeanNames ! = null) { autowiredBeanNames.add(autowiredBeanName); } return (instanceCandidate instanceof Class ? descriptor.resolveCandidate(autowiredBeanName, type, this) : instanceCandidate); } finally { ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); }}Copy the code

#2 Parse the Value of the @Value annotation. If a placeholder is used here to reference the configuration file, The values in the default PropertyPlaceholderConfigurer# PlaceholderResolvingStringValueResolver parsing the configuration file. Is accomplished is a commonly used Spring BeanFactoryPostProcessor, it can be started in the Spring, #3 By default, SimpleTypeConverter is used to convert the configuration value to the type required by the property (for example, if the property is of type int, The TypeConverterDelegate#convertIfNecessary method will eventually be reused as well. #5 Handle @autowire with the corresponding TypeConverter. #6 There are multiple candidate beans. Spring decides which one to use according to priority.

DefaultListableBeanFactory#findAutowireCandidates

protected Map<String, Object> findAutowireCandidates( String beanName, Class<? > requiredType, DependencyDescriptor descriptor) { // #1 String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this, requiredType, true, descriptor.isEager()); Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length); // #2 for (Class<? > autowiringType : this.resolvableDependencies.keySet()) { if (autowiringType.isAssignableFrom(requiredType)) { Object autowiringValue = this.resolvableDependencies.get(autowiringType); autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType); if (requiredType.isInstance(autowiringValue)) { result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue); break; } } } //#3 for (String candidate : candidateNames) { if (! isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) { addCandidateEntry(result, candidate, descriptor, requiredType); }}... return result; }Copy the code

#1 Find all beans in the Spring context by type #2 Find beans found in step 1 from existing dependencies #3 walk through Autowire if it meets the requirements of @Qualifier, add it to the result to check if the candidate class type matches. Don’t go further, interested students read the source code.

Let’s take a look at how Spring handles @AutoWired when there are multiple candidate beans. DefaultListableBeanFactory#determineAutowireCandidate

protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) { Class<? > requiredType = descriptor.getDependencyType(); // #1 String primaryCandidate = determinePrimaryCandidate(candidates, requiredType); if (primaryCandidate ! = null) { return primaryCandidate; } // #2 String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType); if (priorityCandidate ! = null) { return priorityCandidate; } // #3 for (Map.Entry<String, Object> entry : candidates.entrySet()) { String candidateName = entry.getKey(); Object beanInstance = entry.getValue(); if ((beanInstance ! = null && this.resolvableDependencies.containsValue(beanInstance)) || matchesBeanName(candidateName, descriptor.getDependencyName())) { return candidateName; } } return null; }Copy the code

1 Find if the candidate bean has the @primary annotation, and if so, use the bean. Limitation: you can only annotate @primary on one candidate bean. 2 If the candidate bean is created on the Class using the @Component, @Service, @Controller annotation and the @priority annotation, the bean with the lowest Priority value is taken (the lower the Priority value is, the higher the Priority value is). Candidate beans tagged with @Priority have higher Priority than all candidate beans not tagged with @Priority. Limitations that cannot be used for beans generated by methods of the @bean annotation. 3 If the field name is the same as the beanName or bean alias, the bean is returned

When using the @AutoWired annotation, consider that there are multiple bean scenarios of the same type, preferably with the same field name as the target beanName or with the @qualifier annotation.

If you think this article is good, welcome to pay attention to my wechat public number, your attention is my motivation!