Spring中的依賴注入DI源碼詳細(xì)解析
前言
Spring的依賴注入(Dependency Injection,DI)是Spring框架核心的一部分,它是實現(xiàn)控制反轉(zhuǎn)(Inversion of Control,IoC)的一種方式。
依賴注入可以幫助我們減少代碼的耦合度,提高模塊間的獨立性和可測試性。
Spring框架的依賴注入主要發(fā)生在Spring容器初始化應(yīng)用上下文時
Spring容器的主要步驟
先說說Spring容器生命周期的一些主要步驟和依賴注入的階段:
- 配置階段:在這個階段,Spring容器會讀取配置文件(例如XML配置文件或者使用注解的配置類),并根據(jù)這些配置信息創(chuàng)建Bean定義。Bean定義包含了創(chuàng)建和裝配一個Bean所需要的所有信息。
- 實例化階段:在這個階段,Spring容器會根據(jù)Bean定義創(chuàng)建Bean實例。這通常是通過調(diào)用Bean的構(gòu)造函數(shù)或工廠方法來完成的。
- 依賴注入階段:在這個階段,Spring容器會將依賴注入到已經(jīng)創(chuàng)建的Bean實例中。這通常是通過調(diào)用Bean的setter方法或者通過反射直接設(shè)置字段值來完成的。這個階段也可能會觸發(fā)更多的Bean的創(chuàng)建和注入,如果被注入的Bean依賴于其他的Bean的話。
- 初始化階段:在這個階段,Spring容器會調(diào)用Bean的初始化方法。這通常是通過調(diào)用Bean實現(xiàn)的InitializingBean接口的afterPropertiesSet方法或者調(diào)用在配置中指定的自定義初始化方法來完成的。
- 使用階段:在這個階段,應(yīng)用代碼可以開始使用已經(jīng)初始化和裝配好的Bean了。
- 銷毀階段:在這個階段,當(dāng)應(yīng)用上下文被關(guān)閉時,Spring容器會調(diào)用Bean的銷毀方法。這通常是通過調(diào)用Bean實現(xiàn)的DisposableBean接口的destroy方法或者調(diào)用在配置中指定的自定義銷毀方法來完成的。
總的來說,Spring的依賴注入主要發(fā)生在Spring容器初始化應(yīng)用上下文的過程中,具體是在實例化階段之后,初始化階段之前的依賴注入階段。
依賴注入在Spring框架中的觸發(fā)點
先看看觸發(fā)依賴注入方法調(diào)用堆棧的流程圖:
- AbstractApplicationContext#refresh():這個方法是整個Spring應(yīng)用上下文刷新的入口點,包括Bean的創(chuàng)建和初始化。
- AbstractApplicationContext#finishBeanFactoryInitialization(ConfigurableListableBeanFactory): 這個方法會預(yù)實例化所有的單例Bean。
- DefaultListableBeanFactory#preInstantiateSingletons(): 這個方法會遍歷所有的Bean定義,對于每個非懶加載的單例Bean,通過getBean()方法獲取Bean的實例。
- AbstractBeanFactory#getBean(String): 這個方法會檢查是否已經(jīng)存在一個Bean的實例,如果沒有,那么它會觸發(fā)Bean的創(chuàng)建過程。
- AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[]): 這個方法會創(chuàng)建一個新的Bean實例。
- AbstractAutowireCapableBeanFactory#doCreateBean(String, RootBeanDefinition, Object[]): 這個方法會創(chuàng)建一個新的Bean實例,填充Bean的屬性(也就是依賴注入),并調(diào)用Bean的初始化方法。
- AbstractAutowireCapableBeanFactory#populateBean(String, RootBeanDefinition, BeanWrapper): 這個方法會進(jìn)行依賴注入,它會遍歷Bean的所有屬性,對于每個屬性,如果它有一個匹配的Bean定義或者已存在的Bean實例,那么這個Bean會被注入到屬性中。
源碼解析
下面是依賴注入的主要代碼
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { if (bw == null) { if (mbd.hasPropertyValues()) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance"); } else { // Skip property population phase for null instance. return; } } // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the // state of the bean before properties are set. This can be used, for example, // to support styles of field injection. // 實例化之后,屬性設(shè)置之前 if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { return; } } } PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); int resolvedAutowireMode = mbd.getResolvedAutowireMode(); if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) { // MutablePropertyValues是PropertyValues具體的實現(xiàn)類 MutablePropertyValues newPvs = new MutablePropertyValues(pvs); // Add property values based on autowire by name if applicable. if (resolvedAutowireMode == AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } // Add property values based on autowire by type if applicable. if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); PropertyDescriptor[] filteredPds = null; if (hasInstAwareBpps) { if (pvs == null) { pvs = mbd.getPropertyValues(); } for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { // 這里會調(diào)用AutowiredAnnotationBeanPostProcessor的postProcessProperties()方法,會直接給對象中的屬性賦值 // AutowiredAnnotationBeanPostProcessor內(nèi)部并不會處理pvs,直接返回了 PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { return; } } pvs = pvsToUse; } } if (needsDepCheck) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } checkDependencies(beanName, mbd, filteredPds, pvs); } // 如果當(dāng)前Bean中的BeanDefinition中設(shè)置了PropertyValues,那么最終將是PropertyValues中的值,覆蓋@Autowired if (pvs != null) { applyPropertyValues(beanName, mbd, bw, pvs); } }
先是判斷BeanWrapper是否為空,為空則拋出異常,然后再進(jìn)行實例化之后的步驟。
也就是執(zhí)行postProcessAfterInstantiation(Object bean, String beanName)方法
這里是獲取該Bean的自動裝配模式,然后基于不同的裝配模式添加屬性值,Spring的自動裝配(autowire)有以下幾種模式:
- no:這是默認(rèn)的設(shè)置,意味著沒有自動裝配,bean之間的關(guān)系需要通過 或 顯式配置。
- byName:Spring容器通過bean的名稱自動裝配屬性。如果一個bean的屬性名稱與另一個bean的名稱相同,那么它們將被自動裝配。
- byType:Spring容器通過類型自動裝配屬性。如果一個bean的屬性類型與另一個bean的類型相同,那么它們將被自動裝配。如果有多個相同類型的bean,則會拋出異常。
- constructor:類似于 byType,但是適用于構(gòu)造函數(shù)。如果容器中存在不止一個與構(gòu)造函數(shù)參數(shù)相同類型的bean,則會拋出異常。
- autodetect:Spring首先嘗試通過構(gòu)造函數(shù)自動裝配,如果不能通過構(gòu)造函數(shù)自動裝配,那么Spring會嘗試通過 byType 模式自動裝配。
自動裝配雖然可以減少配置的復(fù)雜性,但也可能引入歧義性。因此,對于大型項目,通常推薦使用顯式裝配。
后面會根據(jù)緩存的注入點進(jìn)行注入。injectionMetadataCache就是用來緩存注入點的。
在Spring框架中,injectionMetadataCache是AutowiredAnnotationBeanPostProcessor類(以及它的子類,如CommonAnnotationBeanPostProcessor)的一個成員變量。
尋找注入點的過程主要發(fā)生在AutowiredAnnotationBeanPostProcessor類的findAutowiringMetadata方法中。這個方法首先會嘗試從injectionMetadataCache中獲取指定類的InjectionMetadata。如果沒有找到,就會創(chuàng)建一個新的InjectionMetadata,并將其添加到injectionMetadataCache中。
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) { // Fall back to class name as cache key, for backwards compatibility with custom callers. String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); // Quick check on the concurrent map first, with minimal locking. 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; }
最主要的是通過類解析注入點的過程,buildAutowiringMetadata方法會通過類解析得到注入點的元數(shù)據(jù)。
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) { // 如果一個Bean的類型是String...,那么則根本不需要進(jìn)行依賴注入 if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) { return InjectionMetadata.EMPTY; } List<InjectionMetadata.InjectedElement> elements = new ArrayList<>(); Class<?> targetClass = clazz; do { final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>(); // 遍歷targetClass中的所有Field ReflectionUtils.doWithLocalFields(targetClass, field -> { // field上是否存在@Autowired、@Value、@Inject中的其中一個 MergedAnnotation<?> ann = findAutowiredAnnotation(field); if (ann != null) { // static filed不是注入點,不會進(jìn)行自動注入 if (Modifier.isStatic(field.getModifiers())) { if (logger.isInfoEnabled()) { logger.info("Autowired annotation is not supported on static fields: " + field); } return; } // 構(gòu)造注入點 boolean required = determineRequiredStatus(ann); currElements.add(new AutowiredFieldElement(field, required)); } }); // 遍歷targetClass中的所有Method ReflectionUtils.doWithLocalMethods(targetClass, method -> { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { return; } // method上是否存在@Autowired、@Value、@Inject中的其中一個 MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod); if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { // static method不是注入點,不會進(jìn)行自動注入 if (Modifier.isStatic(method.getModifiers())) { if (logger.isInfoEnabled()) { logger.info("Autowired annotation is not supported on static methods: " + method); } return; } // set方法最好有入?yún)? if (method.getParameterCount() == 0) { if (logger.isInfoEnabled()) { logger.info("Autowired annotation should only be used on methods with parameters: " + method); } } boolean required = determineRequiredStatus(ann); PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); currElements.add(new AutowiredMethodElement(method, required, pd)); } }); elements.addAll(0, currElements); targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); return InjectionMetadata.forElements(elements, clazz); }
當(dāng)找到所有注入點就會返回。通過注入點注入主要是InstantiationAwareBeanPostProcessor接口中的PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)方法實現(xiàn),在具體實現(xiàn)類AutowiredAnnotationBeanPostProcessor中找到了注入點的元數(shù)據(jù),就開始注入。
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { // 找注入點(所有被@Autowired注解了的Field或Method) 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; }
注入的元素會有兩種,一種是類的字段,還有一種是類的方法,不同類型的元素有不同的注入方法:
這是字段的注入
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { Field field = (Field) this.member; Object value; if (this.cached) { // 對于原型Bean,第一次創(chuàng)建的時候,也找注入點,然后進(jìn)行注入,此時cached為false,注入完了之后cached為true // 第二次創(chuàng)建的時候,先找注入點(此時會拿到緩存好的注入點),也就是AutowiredFieldElement對象,此時cache為true,也就進(jìn)到此處了 // 注入點內(nèi)并沒有緩存被注入的具體Bean對象,而是beanName,這樣就能保證注入到不同的原型Bean對象 try { value = resolvedCachedArgument(beanName, this.cachedFieldValue); } catch (NoSuchBeanDefinitionException ex) { // Unexpected removal of target bean for cached argument -> re-resolve value = resolveFieldValue(field, bean, beanName); } } else { // 根據(jù)filed從BeanFactory中查到的匹配的Bean對象 value = resolveFieldValue(field, bean, beanName); } // 反射給filed賦值 if (value != null) { ReflectionUtils.makeAccessible(field); field.set(bean, value); } }
這是方法的注入 方法的注入首先會去找到該方法參數(shù)的Bean對象,然后利用反射調(diào)用該方法
@Override protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // 如果pvs中已經(jīng)有當(dāng)前注入點的值了,則跳過注入 if (checkPropertySkipping(pvs)) { return; } Method method = (Method) this.member; Object[] arguments; if (this.cached) { try { arguments = resolveCachedArguments(beanName); } catch (NoSuchBeanDefinitionException ex) { // Unexpected removal of target bean for cached argument -> re-resolve arguments = resolveMethodArguments(method, bean, beanName); } } else { arguments = resolveMethodArguments(method, bean, beanName); } if (arguments != null) { try { ReflectionUtils.makeAccessible(method); method.invoke(bean, arguments); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } }
到此這篇關(guān)于Spring中的依賴注入DI源碼詳細(xì)解析的文章就介紹到這了,更多相關(guān)依賴注入DI源碼解析內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解MybatisPlus集成nacos導(dǎo)致druid連接不上數(shù)據(jù)庫
這篇文章主要介紹了詳解MybatisPlus集成nacos導(dǎo)致druid連接不上數(shù)據(jù)庫,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11使用Mybatis如何實現(xiàn)刪除多個數(shù)據(jù)
這篇文章主要介紹了使用Mybatis如何實現(xiàn)刪除多個數(shù)據(jù),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03Maven多模塊之父子關(guān)系的創(chuàng)建
這篇文章主要介紹了Maven多模塊之父子關(guān)系的創(chuàng)建,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03Spring Boot中使用Server-Sent Events (SSE) 實
Server-Sent Events (SSE) 是HTML5引入的一種輕量級的服務(wù)器向瀏覽器客戶端單向推送實時數(shù)據(jù)的技術(shù),本文主要介紹了Spring Boot中使用Server-Sent Events (SSE) 實現(xiàn)實時數(shù)據(jù)推送教程,具有一定的參考價值,感興趣的可以了解一下2024-03-03springboot?pom文件加入監(jiān)控依賴后沒有起作用的解決
這篇文章主要介紹了springboot?pom文件加入監(jiān)控依賴后沒有起作用的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02