Spring原始碼之自動裝配
引言
我們使用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 ,驗證了前面的猜測。真是太不容易,趕緊設定快取