Spring中的依賴注入DI源碼詳細(xì)解析
前言
Spring的依賴注入(Dependency Injection,DI)是Spring框架核心的一部分,它是實(shí)現(xiàn)控制反轉(zhuǎn)(Inversion of Control,IoC)的一種方式。
依賴注入可以幫助我們減少代碼的耦合度,提高模塊間的獨(dú)立性和可測(cè)試性。
Spring框架的依賴注入主要發(fā)生在Spring容器初始化應(yīng)用上下文時(shí)
Spring容器的主要步驟
先說說Spring容器生命周期的一些主要步驟和依賴注入的階段:
- 配置階段:在這個(gè)階段,Spring容器會(huì)讀取配置文件(例如XML配置文件或者使用注解的配置類),并根據(jù)這些配置信息創(chuàng)建Bean定義。Bean定義包含了創(chuàng)建和裝配一個(gè)Bean所需要的所有信息。
- 實(shí)例化階段:在這個(gè)階段,Spring容器會(huì)根據(jù)Bean定義創(chuàng)建Bean實(shí)例。這通常是通過調(diào)用Bean的構(gòu)造函數(shù)或工廠方法來完成的。
- 依賴注入階段:在這個(gè)階段,Spring容器會(huì)將依賴注入到已經(jīng)創(chuàng)建的Bean實(shí)例中。這通常是通過調(diào)用Bean的setter方法或者通過反射直接設(shè)置字段值來完成的。這個(gè)階段也可能會(huì)觸發(fā)更多的Bean的創(chuàng)建和注入,如果被注入的Bean依賴于其他的Bean的話。
- 初始化階段:在這個(gè)階段,Spring容器會(huì)調(diào)用Bean的初始化方法。這通常是通過調(diào)用Bean實(shí)現(xiàn)的InitializingBean接口的afterPropertiesSet方法或者調(diào)用在配置中指定的自定義初始化方法來完成的。
- 使用階段:在這個(gè)階段,應(yīng)用代碼可以開始使用已經(jīng)初始化和裝配好的Bean了。
- 銷毀階段:在這個(gè)階段,當(dāng)應(yīng)用上下文被關(guān)閉時(shí),Spring容器會(huì)調(diào)用Bean的銷毀方法。這通常是通過調(diào)用Bean實(shí)現(xiàn)的DisposableBean接口的destroy方法或者調(diào)用在配置中指定的自定義銷毀方法來完成的。
總的來說,Spring的依賴注入主要發(fā)生在Spring容器初始化應(yīng)用上下文的過程中,具體是在實(shí)例化階段之后,初始化階段之前的依賴注入階段。
依賴注入在Spring框架中的觸發(fā)點(diǎn)
先看看觸發(fā)依賴注入方法調(diào)用堆棧的流程圖:
- AbstractApplicationContext#refresh():這個(gè)方法是整個(gè)Spring應(yīng)用上下文刷新的入口點(diǎn),包括Bean的創(chuàng)建和初始化。
- AbstractApplicationContext#finishBeanFactoryInitialization(ConfigurableListableBeanFactory): 這個(gè)方法會(huì)預(yù)實(shí)例化所有的單例Bean。
- DefaultListableBeanFactory#preInstantiateSingletons(): 這個(gè)方法會(huì)遍歷所有的Bean定義,對(duì)于每個(gè)非懶加載的單例Bean,通過getBean()方法獲取Bean的實(shí)例。
- AbstractBeanFactory#getBean(String): 這個(gè)方法會(huì)檢查是否已經(jīng)存在一個(gè)Bean的實(shí)例,如果沒有,那么它會(huì)觸發(fā)Bean的創(chuàng)建過程。
- AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[]): 這個(gè)方法會(huì)創(chuàng)建一個(gè)新的Bean實(shí)例。
- AbstractAutowireCapableBeanFactory#doCreateBean(String, RootBeanDefinition, Object[]): 這個(gè)方法會(huì)創(chuàng)建一個(gè)新的Bean實(shí)例,填充Bean的屬性(也就是依賴注入),并調(diào)用Bean的初始化方法。
- AbstractAutowireCapableBeanFactory#populateBean(String, RootBeanDefinition, BeanWrapper): 這個(gè)方法會(huì)進(jìn)行依賴注入,它會(huì)遍歷Bean的所有屬性,對(duì)于每個(gè)屬性,如果它有一個(gè)匹配的Bean定義或者已存在的Bean實(shí)例,那么這個(gè)Bean會(huì)被注入到屬性中。
源碼解析
下面是依賴注入的主要代碼
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í)例化之后,屬性設(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具體的實(shí)現(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) { // 這里會(huì)調(diào)用AutowiredAnnotationBeanPostProcessor的postProcessProperties()方法,會(huì)直接給對(duì)象中的屬性賦值 // AutowiredAnnotationBeanPostProcessor內(nèi)部并不會(huì)處理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)行實(shí)例化之后的步驟。
也就是執(zhí)行postProcessAfterInstantiation(Object bean, String beanName)方法
這里是獲取該Bean的自動(dòng)裝配模式,然后基于不同的裝配模式添加屬性值,Spring的自動(dòng)裝配(autowire)有以下幾種模式:
- no:這是默認(rèn)的設(shè)置,意味著沒有自動(dòng)裝配,bean之間的關(guān)系需要通過 或 顯式配置。
- byName:Spring容器通過bean的名稱自動(dòng)裝配屬性。如果一個(gè)bean的屬性名稱與另一個(gè)bean的名稱相同,那么它們將被自動(dòng)裝配。
- byType:Spring容器通過類型自動(dòng)裝配屬性。如果一個(gè)bean的屬性類型與另一個(gè)bean的類型相同,那么它們將被自動(dòng)裝配。如果有多個(gè)相同類型的bean,則會(huì)拋出異常。
- constructor:類似于 byType,但是適用于構(gòu)造函數(shù)。如果容器中存在不止一個(gè)與構(gòu)造函數(shù)參數(shù)相同類型的bean,則會(huì)拋出異常。
- autodetect:Spring首先嘗試通過構(gòu)造函數(shù)自動(dòng)裝配,如果不能通過構(gòu)造函數(shù)自動(dòng)裝配,那么Spring會(huì)嘗試通過 byType 模式自動(dòng)裝配。
自動(dòng)裝配雖然可以減少配置的復(fù)雜性,但也可能引入歧義性。因此,對(duì)于大型項(xiàng)目,通常推薦使用顯式裝配。
后面會(huì)根據(jù)緩存的注入點(diǎn)進(jìn)行注入。injectionMetadataCache就是用來緩存注入點(diǎn)的。
在Spring框架中,injectionMetadataCache是AutowiredAnnotationBeanPostProcessor類(以及它的子類,如CommonAnnotationBeanPostProcessor)的一個(gè)成員變量。
尋找注入點(diǎn)的過程主要發(fā)生在AutowiredAnnotationBeanPostProcessor類的findAutowiringMetadata方法中。這個(gè)方法首先會(huì)嘗試從injectionMetadataCache中獲取指定類的InjectionMetadata。如果沒有找到,就會(huì)創(chuàng)建一個(gè)新的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); } // 解析注入點(diǎn)并緩存 metadata = buildAutowiringMetadata(clazz); this.injectionMetadataCache.put(cacheKey, metadata); } } } return metadata; }
最主要的是通過類解析注入點(diǎn)的過程,buildAutowiringMetadata方法會(huì)通過類解析得到注入點(diǎn)的元數(shù)據(jù)。
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) { // 如果一個(gè)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中的其中一個(gè) MergedAnnotation<?> ann = findAutowiredAnnotation(field); if (ann != null) { // static filed不是注入點(diǎn),不會(huì)進(jìn)行自動(dòng)注入 if (Modifier.isStatic(field.getModifiers())) { if (logger.isInfoEnabled()) { logger.info("Autowired annotation is not supported on static fields: " + field); } return; } // 構(gòu)造注入點(diǎn) 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中的其中一個(gè) MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod); if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { // static method不是注入點(diǎn),不會(huì)進(jìn)行自動(dòng)注入 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)找到所有注入點(diǎn)就會(huì)返回。通過注入點(diǎn)注入主要是InstantiationAwareBeanPostProcessor接口中的PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)方法實(shí)現(xiàn),在具體實(shí)現(xiàn)類AutowiredAnnotationBeanPostProcessor中找到了注入點(diǎn)的元數(shù)據(jù),就開始注入。
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { // 找注入點(diǎn)(所有被@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; }
注入的元素會(huì)有兩種,一種是類的字段,還有一種是類的方法,不同類型的元素有不同的注入方法:
這是字段的注入
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { Field field = (Field) this.member; Object value; if (this.cached) { // 對(duì)于原型Bean,第一次創(chuàng)建的時(shí)候,也找注入點(diǎn),然后進(jìn)行注入,此時(shí)cached為false,注入完了之后cached為true // 第二次創(chuàng)建的時(shí)候,先找注入點(diǎn)(此時(shí)會(huì)拿到緩存好的注入點(diǎn)),也就是AutowiredFieldElement對(duì)象,此時(shí)cache為true,也就進(jìn)到此處了 // 注入點(diǎn)內(nèi)并沒有緩存被注入的具體Bean對(duì)象,而是beanName,這樣就能保證注入到不同的原型Bean對(duì)象 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對(duì)象 value = resolveFieldValue(field, bean, beanName); } // 反射給filed賦值 if (value != null) { ReflectionUtils.makeAccessible(field); field.set(bean, value); } }
這是方法的注入 方法的注入首先會(huì)去找到該方法參數(shù)的Bean對(duì)象,然后利用反射調(diào)用該方法
@Override protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { // 如果pvs中已經(jīng)有當(dāng)前注入點(diǎn)的值了,則跳過注入 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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解MybatisPlus集成nacos導(dǎo)致druid連接不上數(shù)據(jù)庫
這篇文章主要介紹了詳解MybatisPlus集成nacos導(dǎo)致druid連接不上數(shù)據(jù)庫,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11使用Mybatis如何實(shí)現(xiàn)刪除多個(gè)數(shù)據(jù)
這篇文章主要介紹了使用Mybatis如何實(shí)現(xiàn)刪除多個(gè)數(shù)據(jù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03Maven多模塊之父子關(guān)系的創(chuàng)建
這篇文章主要介紹了Maven多模塊之父子關(guān)系的創(chuàng)建,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03Java中雙冒號(hào)運(yùn)算符(::)的用法詳解
在Java 8引入的Lambda表達(dá)式和函數(shù)式接口之后,雙冒號(hào)運(yùn)算符(::)成為了一項(xiàng)重要的功能,下面我們就來學(xué)習(xí)一下Java中的雙冒號(hào)運(yùn)算符及其常見應(yīng)用場(chǎng)景吧2023-12-12Spring Boot中使用Server-Sent Events (SSE) 實(shí)
Server-Sent Events (SSE) 是HTML5引入的一種輕量級(jí)的服務(wù)器向?yàn)g覽器客戶端單向推送實(shí)時(shí)數(shù)據(jù)的技術(shù),本文主要介紹了Spring Boot中使用Server-Sent Events (SSE) 實(shí)現(xiàn)實(shí)時(shí)數(shù)據(jù)推送教程,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03springboot?pom文件加入監(jiān)控依賴后沒有起作用的解決
這篇文章主要介紹了springboot?pom文件加入監(jiān)控依賴后沒有起作用的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02