引言
我們使用Spring開發過程中經常會用到Autowired注解注入依賴的bean,這部分也是面試的熱點問題之一。今天咱們一起來深入研究下自動注入的背后實現原理。首先上一個例子,如下所示:
@RestController public class TestController { @Autowired List<ICheckRuleService> checkRuleService; @RequestMapping("/test") public void test(){ checkRuleService.forEach(x->x.valid()); } }
從填充Bean開始
Autowired是怎么實現自動注入的呢,今天我們來通過源碼分析一下。當Spring創建 TestController Bean時,會調用AbstractBeanFactory#doGetBean(如果對Spring創建Bean流程不熟的讀者,可以給我留言,后面考慮是否寫個IOC系列),doGetBean里面會調用doCreateBean()方法去創建Bean,創建Bean之后,會對Bean進行填充
try { this.populateBean(beanName, mbd, instanceWrapper); exposedObject = this.initializeBean(beanName, exposedObject, mbd); }
populateBean 里有這樣一段代碼,看起來是處理Autowired的,分別是autowireByName 和 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; }
我們來驗證一下,通過斷點調試我們發現並不會進入if里,所以自動注入並不是這里實現的。那這里有什么用呢,先放一放,后面再說。
后置處理器屬性填充
那么到底是哪里注入進去的呢?我們繼續往下看,在這段代碼下方有個BeanPostProcessor的邏輯,通過斷點我們發現有個AutowiredAnnotationBeanPostProcessor 的后置處理器,當這個BeanPostProcessor執行完 postProcessPropertyValues方法后,testController的checkRuleService 屬性就有了值了,說明屬性值注入肯定和 AutowiredAnnotationBeanPostProcessor 有關,我們跟進去看一下
進入AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues 方法里,里面主要有兩部分邏輯
-
首先看到一段 findAutowiringMetadata 的邏輯,根據方法名稱知道是獲取當前bean的注入元信息
-
調用 metadata.inject 注入屬性
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); } }
我們先來看第一部分:findAutowiringMetadata
我們進入findAutowiringMetadata,看下它的邏輯,先從 injectionMetadataCache 緩存里取,如果取不到值,則調用buildAutowiringMetadata 構建 InjectionMetadata ,構建成功后設置到緩存里。
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; }
我們來看下 buildAutowiringMetadata,繼續跟進去,源碼如下:
里面是通過當前Bean的Class反射獲取 Field 和 Method ,然后對 Field 和 Method 分別調 findAutowiredAnnotation 方法獲取自動注入的注解,然后根據注解類型是否required構建不同類型的InjectedElement。
-
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));
補充:通過AutowiredAnnotationBeanPostProcessor 構造函數我們知道,自動注入處理的是被 @Autowired 和 @Value 這兩個注解標注的屬性(Field)或方法(Method):
public AutowiredAnnotationBeanPostProcessor() { this.autowiredAnnotationTypes.add(Autowired.class); this.autowiredAnnotationTypes.add(Value.class); //......
到這里,需要注入的元數據信息就已經構建完成了,接下來就要到注入部分了。來看下 postProcessPropertyValues 的第二部分。
再看第二部分:metadata.inject
前面獲取到了需要注入的元數據信息,接下來是元數據 inject 的實現,繼續跟進去,里面是一個for循環,循環調用了element的inject方法
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); } } }
我們斷點調試進去,發現element的真實類型是AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement,而當前element 真實類型是 TestController.checkRuleService 的集合。
我們進入AutowiredFieldElement#inject方法,首先嘗試從緩存里拿當前Field的值,肯定拿不到,所以走的是else分支,else分支里從beanFactory里解析當前Field屬性值
value = AutowiredAnnotationBeanPostProcessor.this.beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
繼續跟進去,發現其實調用的 doResolveDependency 方法
越來越接近真相了,不要着急,繼續跟進去
發現一個類型為Object的 multipleBeans ,結果返回的也是這個Object,我們大膽猜測這個Object就是我們需要注入的List屬性,繼續跟進去驗證一下:
我們看一下 Collection 分支的源碼
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; } } }
里面是調用了 findAutowireCandidates 來獲取Bean,findAutowireCandidates 內部會獲取到依賴的BeanNames,然后根據beanName 循環調用beanFactory#getBean 獲取需要注入的bean
this.findAutowireCandidates(beanName,elementType,new DefaultListableBeanFactory.MultiElementDescriptor(descriptor))
beanFactory#getBean方法,最終會調用 AbstractBeanFactory#doGetBean,獲取到需要裝配進去的屬性bean。
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) throws BeansException { return beanFactory.getBean(beanName); }
當所有的循環執行完畢,就獲取到了 multipleBeans ,驗證了前面的猜測。真是太不容易,趕緊設置緩存 😄
最終通過field.set 將獲取到的List屬性值value設置到當前bean里,代碼如下:
if (value != null) { ReflectionUtils.makeAccessible(field); field.set(bean, value); }
執行field的set方法后,再來看checkRuleService屬性就有值了
如果是Method注入,對應的就是通過反射調用 method.invoke 將屬性設置到方法參數里,大致流程差不多。到此,Autowired 裝配流程也就結束了。
前面在講到 populateBean 的時候,有個根據 autowireMode 判斷是否執行屬性注入,當時獲取的autowireMode==0,那么什么時候autowireMode 會有值並且會根據autowireByName 和 autowireByType來裝配呢?
protected void populateBean(String beanName, RootBeanDefinition mbd,
其實也很好理解,通過源碼我們知道,這里的 mbd 是一個 RootBeanDefinition ,也就是說這里的 mbd.getResolvedAutowireMode()獲取的值是通過Bean定義或者通過PostProcessor拿到BeanDefinition,然后設置了AutowireMode屬性才會有值。當我們查看這里的autowireByType源碼(AbstractAutowireCapableBeanFactory#autowireByType)可以發現,其實autowireByType也是會調用resolveDependency,繼續跟進去,發現其實調用的 doResolveDependency 方法,而AutowiredAnnotationBeanPostProcessor 也是通過這個方法實現的自動注入,后面的流程就都一樣了。
最后總結一下
1、bean創建完成后,會調用 populateBean() 填充Bean,在populateBean()方法里會獲取所有的BeanPostProcessor,並循環執行 BeanPostProcessor#postProcessPropertyValues() 設置屬性
2、其中有個AutowiredAnnotationBeanPostProcessor,這個處理器里會根據當前Bean的Class,通過反射獲取 Field 和 Method ,分別獲取 Field 和 Method 上的自動注入的注解(@Autowired 和 @Value),構建注入元數據InjectionMetadata
3、調用注入元數據InjectionMetadata的 inject() 方法,裝配屬性(有兩種:AutowiredFieldElement 和AutowiredMethodElement),會調用this.beanFactory.resolveDependency(desc,beanName,autowiredBeanNames, typeConverter) 解析依賴的屬性值
4、resolveDependency 最終會調用到 resolveMultipleBeans ,而 resolveMultipleBeans 會根據當前注入屬性的類型分別按 Array、Collection、Map 走不同的分支,在分支里調用 findAutowireCandidates 獲取注入bean的實例,最終回調到 AbstractBeanFactory#doGetBean
5、獲取到所有需要注入的屬性 bean 實例后,通過反射設置到對應的屬性或方法里去,就完成了自動注入全流程了