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-03
Maven多模塊之父子關(guān)系的創(chuàng)建
這篇文章主要介紹了Maven多模塊之父子關(guān)系的創(chuàng)建,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
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
springboot?pom文件加入監(jiān)控依賴后沒有起作用的解決
這篇文章主要介紹了springboot?pom文件加入監(jiān)控依賴后沒有起作用的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02

