Java Autowired註解深入分析
今天是正月初八,先祝大傢新年快樂。前幾天遇見瞭一次Autowired註入失敗的問題,所以找時間研究瞭一下相關的Spring源碼,分享一下。如果哪位大佬發現問題,請幫忙反饋。
分享之前,先給一個小建議。Spring源碼龐大,其中的擴展點眾多,貿然全篇吸收,很容易勸退。我的思路是想看哪部分知識,就隻看和這部分相關的,其他細節,先放放,隻捋主線,這樣做,效率比較高。
下面進入正文。
我寫瞭一個非常簡單的web工程,便於調試源碼。測試代碼如下:
Controller代碼
@RestController public class HomeController { @Autowired private HomeService homeService; @RequestMapping("/index") public String index() { return homeService.testService(); } }
Service代碼
//接口 public interface HomeService { String testService(); } //實現類 @Service public class HomeServiceImpl implements HomeService{ @Override public String testService(){ return "I'm a test service"; } }
我們的目的是:把HomeService通過Autowired註解註入HomeController中,從而在index方法中實現對HomeService方法的內部調用。
我們想探究的是:
為什麼我們給字段加一個Autowired註解,Spring就知道需要給bean註入這個字段對應的服務呢?
想知道為什麼Spring知道給HomeController中註入另外一個bean,我們肯定就得看HomeController,這個bean是怎麼創建出來的。
直接看AbstractAutowireCapableBeanFactory方法的doCreateBean方法,裡面是Spring創建bean的邏輯。有2個地方和Autowired有關系。
我去掉瞭無關邏輯,隻留下瞭和Autowired有關系的邏輯
1、applyMergedBeanDefinitionPostProcessors()
2、populateBean(beanName, mbd, instanceWrapper)
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { //遍歷所有的beanPostProcessor,尋找bean中被Autowired註解修飾的成員變量 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; } } Object exposedObject = bean; try { //給bean填充其他bean。對應到我們的demo,就是給HomeController註入HomeService。 populateBean(beanName, mbd, instanceWrapper); } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } }
applyMergedBeanDefinitionPostProcessors方法,這個方法的作用是尋找HomeController的字段裡,有沒有哪個字段添加瞭Autowired註解。
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) { for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) { processor.postProcessMergedBeanDefinition(mbd, beanType, beanName); } }
這段邏輯實際上是在遍歷容器中的BeanPostProcessor,然後執行BeanPostProcessor中的邏輯,我們需要關註的是AutowiredAnnotationBeanPostProcessor,這個類是實現Autowired邏輯的核心類,大傢重點關註。
@Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null); metadata.checkConfigMembers(beanDefinition); }
可以看到邏輯很少。第一行代碼就是找HomeController中被Autowired註解修飾的字段。
我們進入findAutowiringMetadata中看一下。其中有一個內部方法調用是buildAutowiringMetadata(clazz),我們再進入這個方法
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) { ReflectionUtils.doWithLocalFields(targetClass, field -> { //尋找字段上有被Autowired註解修飾的字段 MergedAnnotation<?> ann = findAutowiredAnnotation(field); if (ann != null) { //static修飾的靜態字段不能進行註入 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)); } }); return InjectionMetadata.forElements(elements, clazz); }
以上邏輯執行完,我們就拿到瞭待註入的homeService的元信息對象,即:InjectionMetadata。
上面邏輯中有一行代碼,是判斷字段是否被static修飾,如果是,不可以被註入。
小節一下。
繞瞭這麼一圈,其實很簡單。就是Spring在創建HomeController這個bean的時候,會檢測其成員字段有沒有被Autowired修飾。
對應到我們的demo代碼,我們知道瞭HomeController中有一個被Autowired註解修飾的字段HomeService。
接下來,之後就是給HomeController註入這個HomeService。
populateBean(beanName, mbd, instanceWrapper);
我們進入這個方法看一下。
重要邏輯是:pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
對應的實現類還是AutowiredAnnotationBeanPostProcessor。註入的核心邏輯是
@Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { metadata.inject(bean, beanName, pvs); } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } return pvs; }
隻有兩行邏輯。
第一行:InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
這行代碼,我們上面說到過,第一次執行時,是為瞭尋找被Autowired修飾字段的元信息。然後放入瞭緩存中。這一次再執行,我們就是要從緩存中拿到待註入的數據元信息InjectionMetadata。
第二行:
開始註入。
先看第一行代碼的具體邏輯,獲取待註入的數據元信息。
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) { //構建緩存key String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); //第一次執行,緩存中沒有數據,先構建,然後放入緩存。第二次執行有數據,直接從緩存中獲取 InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { synchronized (this.injectionMetadataCache) { metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { if (metadata != null) { metadata.clear(pvs); } //第一次執行,緩存中不存在,先放入緩存。 metadata = buildAutowiringMetadata(clazz); this.injectionMetadataCache.put(cacheKey, metadata); } } } return metadata; }
可以看到,上面有一個雙檢鎖的設計,應該是防止並發情況下,多次創建InjectionMetadata對象。
再看第二行的具體邏輯,metadata.inject(bean, beanName, pvs);核心邏輯在AutowiredAnnotationBeanPostProcessor的626行,inject方法中。重點就是下面的代碼。找到這個待註入的homeService對象,然後用反射註入到HomeController中。
@Override protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { Field field = (Field) this.member; else { //到Spring容器中尋找待註入的bean value = resolveFieldValue(field, bean, beanName); } //找到之後,反射註入 if (value != null) { ReflectionUtils.makeAccessible(field); field.set(bean, value); } }
找的過程,就是先從Spring子容器中找,然後到Spring父容器中找,不詳細說瞭,感興趣的,可以繼續跟進resolveFieldValue方法看。其實,大部分的Autowired註入失敗,都是由於未在Spring容器中找到待註入的bean。所以Spring容器的加載過程很重要,一定要讀讀那部分源碼。
至此,Autowired的主要執行流程,我們就分享完瞭。
總結下來就是:看bean中是否有被Autowired註解修飾的成員變量。如果有,從Spring容器中找到這個成員變量對應的bean,註入進去。
到此這篇關於Java Autowired註解深入分析的文章就介紹到這瞭,更多相關Java Autowired內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Java超詳細分析@Autowired原理
- Java @Autowired註解底層原理詳細分析
- Spring @Autowired註解超詳細示例
- Spring中的註解@Autowired實現過程全解(@Autowired 背後的故事)
- SpringBoot中@Autowired生效方式詳解