欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring中的依賴注入DI源碼詳細(xì)解析

 更新時間:2023年11月30日 09:14:07   作者:啊幾  
這篇文章主要介紹了Spring中的依賴注入DI源碼詳細(xì)解析,Spring的依賴注入(Dependency Injection,DI)是Spring框架核心的一部分,它是實現(xiàn)控制反轉(zhuǎn)(Inversion of Control,IoC)的一種方式,需要的朋友可以參考下

前言

Spring的依賴注入(Dependency Injection,DI)是Spring框架核心的一部分,它是實現(xiàn)控制反轉(zhuǎn)(Inversion of Control,IoC)的一種方式。

依賴注入可以幫助我們減少代碼的耦合度,提高模塊間的獨立性和可測試性。

Spring框架的依賴注入主要發(fā)生在Spring容器初始化應(yīng)用上下文時

Spring容器的主要步驟

先說說Spring容器生命周期的一些主要步驟和依賴注入的階段:

  1. 配置階段:在這個階段,Spring容器會讀取配置文件(例如XML配置文件或者使用注解的配置類),并根據(jù)這些配置信息創(chuàng)建Bean定義。Bean定義包含了創(chuàng)建和裝配一個Bean所需要的所有信息。
  2. 實例化階段:在這個階段,Spring容器會根據(jù)Bean定義創(chuàng)建Bean實例。這通常是通過調(diào)用Bean的構(gòu)造函數(shù)或工廠方法來完成的。
  3. 依賴注入階段:在這個階段,Spring容器會將依賴注入到已經(jīng)創(chuàng)建的Bean實例中。這通常是通過調(diào)用Bean的setter方法或者通過反射直接設(shè)置字段值來完成的。這個階段也可能會觸發(fā)更多的Bean的創(chuàng)建和注入,如果被注入的Bean依賴于其他的Bean的話。
  4. 初始化階段:在這個階段,Spring容器會調(diào)用Bean的初始化方法。這通常是通過調(diào)用Bean實現(xiàn)的InitializingBean接口的afterPropertiesSet方法或者調(diào)用在配置中指定的自定義初始化方法來完成的。
  5. 使用階段:在這個階段,應(yīng)用代碼可以開始使用已經(jīng)初始化和裝配好的Bean了。
  6. 銷毀階段:在這個階段,當(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)用堆棧的流程圖:

在這里插入圖片描述

  1. AbstractApplicationContext#refresh():這個方法是整個Spring應(yīng)用上下文刷新的入口點,包括Bean的創(chuàng)建和初始化。
  2. AbstractApplicationContext#finishBeanFactoryInitialization(ConfigurableListableBeanFactory): 這個方法會預(yù)實例化所有的單例Bean。
  3. DefaultListableBeanFactory#preInstantiateSingletons(): 這個方法會遍歷所有的Bean定義,對于每個非懶加載的單例Bean,通過getBean()方法獲取Bean的實例。
  4. AbstractBeanFactory#getBean(String): 這個方法會檢查是否已經(jīng)存在一個Bean的實例,如果沒有,那么它會觸發(fā)Bean的創(chuàng)建過程。
  5. AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[]): 這個方法會創(chuàng)建一個新的Bean實例。
  6. AbstractAutowireCapableBeanFactory#doCreateBean(String, RootBeanDefinition, Object[]): 這個方法會創(chuàng)建一個新的Bean實例,填充Bean的屬性(也就是依賴注入),并調(diào)用Bean的初始化方法。
  7. 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);
		}![在這里插入圖片描述](https://img-blog.csdnimg.cn/fbf72b3c614c4efdb5b3ad5bfd6f5a6a.png)
	}

先是判斷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)文章

  • SpringBoot + vue2.0查詢所用功能詳解

    SpringBoot + vue2.0查詢所用功能詳解

    這篇文章主要介紹了SpringBoot + vue2.0查詢所用功能,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2023-11-11
  • 詳解MybatisPlus集成nacos導(dǎo)致druid連接不上數(shù)據(jù)庫

    詳解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
  • Spring中的Actuator使用詳解

    Spring中的Actuator使用詳解

    這篇文章主要介紹了Spring中的Actuator使用詳解,在生產(chǎn)環(huán)境中運行的程序,并不總是穩(wěn)定、安靜、正確的,往往會遇到各式各樣的現(xiàn)場狀況,這個時候,就需要獲取該程序足夠多的運行狀態(tài)信息,然后分析并對其進(jìn)行有效管理,需要的朋友可以參考下
    2023-09-09
  • 詳解SpringMVC學(xué)習(xí)系列之國際化

    詳解SpringMVC學(xué)習(xí)系列之國際化

    這篇文章主要介紹了詳解SpringMVC學(xué)習(xí)系列之國際化,詳細(xì)的介紹了關(guān)于瀏覽器,Session,Cookie,URL請求的國際化的實現(xiàn),有興趣的可以了解一下
    2017-07-07
  • 使用Mybatis如何實現(xiàn)刪除多個數(shù)據(jù)

    使用Mybatis如何實現(xiàn)刪除多個數(shù)據(jù)

    這篇文章主要介紹了使用Mybatis如何實現(xiàn)刪除多個數(shù)據(jù),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Maven多模塊之父子關(guān)系的創(chuàng)建

    Maven多模塊之父子關(guān)系的創(chuàng)建

    這篇文章主要介紹了Maven多模塊之父子關(guān)系的創(chuàng)建,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Java中雙冒號運算符(::)的用法詳解

    Java中雙冒號運算符(::)的用法詳解

    在Java 8引入的Lambda表達(dá)式和函數(shù)式接口之后,雙冒號運算符(::)成為了一項重要的功能,下面我們就來學(xué)習(xí)一下Java中的雙冒號運算符及其常見應(yīng)用場景吧
    2023-12-12
  • Spring Boot中使用Server-Sent Events (SSE) 實現(xiàn)實時數(shù)據(jù)推送教程

    Spring 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-03
  • Java之理解多態(tài)詳解

    Java之理解多態(tài)詳解

    大家好,本篇文章主要講的是Java之理解多態(tài)詳解,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • springboot?pom文件加入監(jiān)控依賴后沒有起作用的解決

    springboot?pom文件加入監(jiān)控依賴后沒有起作用的解決

    這篇文章主要介紹了springboot?pom文件加入監(jiān)控依賴后沒有起作用的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02

最新評論