@Autowired
一、基本信息
?? 作者 - Lex ?? 博客 - 我的CSDN ?? 文章目錄 - 所有文章 ?? 源碼地址 - @Autowired源碼
二、注解描述
@Autowired 注解,用于實現(xiàn)依賴注入(Dependency Injection, DI)。當我們在 Spring 中定義了一個 Bean 并想要使用另一個 Bean 時,可以使用 @Autowired 注解來自動注入所需的 Bean,而我們無需手動查找和配置它。
三、接口源碼
@Autowired注解是 Spring 框架自 2.5 版本開始引入的一個核心注解,該注解用于告知 Spring 框架的依賴注入工具自動注入所需的依賴。
/**
* 通過Spring的依賴注入機制標記構造函數、字段、setter方法或配置方法。
* 這是JSR-330 Inject 注解的一種替代,增加了必需與可選的語義。
*
* 自動注入的構造函數:
* 任何給定的bean類只有一個構造函數可以聲明此注解,且其required屬性設置為 true,
* 表示當作為Spring bean使用時要自動注入的構造函數。如果required屬性設置為true,
* 則只能有一個構造函數被標記為Autowired。如果多個非必需的構造函數聲明了這個注解,
* 它們都會被視為自動注入的候選者。Spring會選擇可以滿足最多依賴的構造函數進行注入。如果沒有一個候選者滿足條件,
* 則會使用默認的構造函數。如果一個類聲明了多個構造函數,但沒有一個使用Autowired,
* 則會使用默認的構造函數。如果一個類從一開始就只聲明了一個構造函數,它總是會被使用,即使沒有被標記。被標記的構造函數不需要是public的。
*
* 自動注入的字段:
* 字段會在bean構造完成后注入,任何配置方法調用之前。配置字段不需要是public的。
*
* 自動注入的方法:
* 配置方法可以有任意名稱和任意數量的參數;其中每一個參數都會通過Spring容器中的一個匹配的bean進行自動注入。
* Bean屬性的setter方法在實質上只是這種通用配置方法的一個特例。這些配置方法不需要是public的。
*
* 自動注入的參數:
* 盡管從Spring 5.0開始Autowired可以在方法或構造函數的單獨參數上聲明,
* 但框架的大部分都會忽略這樣的聲明。唯一支持自動注入參數的Spring核心部分是
* spring-test模塊中的JUnit Jupiter支持。
*
* 多參數與'required'語義:
* 對于多參數的構造函數或方法,required屬性適用于所有參數。
*
* 自動裝配數組、集合和映射:
* 對于數組、Collection或Map類型的依賴,容器會自動注入所有匹配聲明的值類型的bean。
* 對于這種目的,map的鍵必須聲明為類型String,它會解析為相應的bean名稱。
*
* 在BeanPostProcessor或BeanFactoryPostProcessor中不支持:
* 請注意,實際的注入是通過BeanPostProcessor執(zhí)行的,這意味著我們不能使用Autowired在
* BeanPostProcessor或BeanFactoryPostProcessor類型中進行注入。
*
* @author Juergen Hoeller
* @author Mark Fisher
* @author Sam Brannen
* @since 2.5
* @see AutowiredAnnotationBeanPostProcessor
* @see Qualifier
* @see Value
*/
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
/**
* 聲明注解的依賴是否是必需的。
* 默認值為true。
*/
boolean required() default true;
}
四、主要功能
-
自動注入依賴
- 不需要明確指定 bean 之間的關系,Spring 會自動找到并注入所需的依賴。
-
字段注入
- 可以直接標記在類的字段上,使得該字段在 Bean 初始化時被自動注入。
-
構造函數注入
- 當標記在構造函數上時,該構造函數會被用于創(chuàng)建 bean 實例并注入所需的依賴。
-
方法注入
- 當標記在 setter 方法或其他方法上時,這些方法會在 Bean 初始化時被調用以注入依賴。
-
指定必需性
- 通過
required屬性,可以指定某個依賴是否是必需的。如果標記為必需但沒有找到相應的依賴,Spring 會拋出異常。
- 通過
五、最佳實踐
首先來看看啟動類入口,上下文環(huán)境使用AnnotationConfigApplicationContext(此類是使用Java注解來配置Spring容器的方式),構造參數我們給定了一個MyConfiguration組件類。然后從Spring上下文中獲取一個MyController類型的bean并調用了showService方法,
public class AutowiredApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
MyController controller = context.getBean(MyController.class);
controller.showService();
}
}
在MyConfiguration類中,使用了@ComponentScan("com.xcs.spring")注解告訴 Spring 在指定的包(在這里是 "com.xcs.spring")及其子包中搜索帶有 @Component、@Service、@Repository 和 @Controller 等注解的類,并將它們自動注冊為 beans。這樣,spring就不必為每個組件明確寫一個 bean 定義。Spring 會自動識別并注冊它們。
@Configuration
@ComponentScan("com.xcs.spring")
public class MyConfiguration {
}
Spring 容器在初始化 MyController 時自動注入一個 MyService 類型的 bean 到 myService 字段。
@Controller
public class MyController {
@Autowired
private MyService myService;
public void showService(){
System.out.println("myService = " + myService);
}
}
MyService 是一個簡單的服務類,但我們沒有定義任何方法或功能。
@Service
public class MyService {
}
運行結果發(fā)現(xiàn),我們使用 @Autowired 注解的功能,在我們的 Spring 上下文中工作正常,并且它成功地自動注入了所需的依賴關系。
myService = com.xcs.spring.service.MyService@4a883b15
六、時序圖
sequenceDiagram
Title: @Autowired注解時序圖
AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:applyMergedBeanDefinitionPostProcessors(mbd,beanType,beanName)<br>應用Bean定義的后置處理器
AbstractAutowireCapableBeanFactory->>AutowiredAnnotationBeanPostProcessor:postProcessMergedBeanDefinition(beanDefinition,beanType,beanName)<br>處理已合并的Bean定義
AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:findAutowiringMetadata(beanName,clazz,pvs)<br>查找自動注入的元數據
AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:buildAutowiringMetadata(clazz)<br>構建自動注入的元數據
AutowiredAnnotationBeanPostProcessor->>ReflectionUtils:doWithLocalFields(clazz,fc)<br>處理類的本地字段
ReflectionUtils->>AutowiredAnnotationBeanPostProcessor:解析有@Autowired注解的字段
AutowiredAnnotationBeanPostProcessor->>ReflectionUtils:doWithLocalMethods(clazz,fc)<br>處理類的本地方法
ReflectionUtils->>AutowiredAnnotationBeanPostProcessor:解析有@Autowired注解的方法
AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:injectionMetadataCache.put(cacheKey, metadata)<br>將元數據存入緩存
AbstractAutowireCapableBeanFactory->>AbstractAutowireCapableBeanFactory:populateBean(beanName,mbd,bw)<br>填充Bean的屬性值
AbstractAutowireCapableBeanFactory->>AutowiredAnnotationBeanPostProcessor:postProcessProperties(pvs,bean,beanName)<br>后處理Bean的屬性
AutowiredAnnotationBeanPostProcessor->>AutowiredAnnotationBeanPostProcessor:findAutowiringMetadata(beanName,clazz,pvs)<br>再次查找自動注入的元數據
Note right of AutowiredAnnotationBeanPostProcessor:<br>從緩存中獲取注入的元數據
AutowiredAnnotationBeanPostProcessor->>InjectionMetadata:inject(bean, beanName, pvs)<br>執(zhí)行實際的屬性注入
InjectionMetadata->>AutowiredFieldElement:inject(target, beanName, pvs)<br>注入特定的字段元素
AutowiredFieldElement->>AutowiredFieldElement:resolveFieldValue(field,bean,beanName)<br>解析字段的值
AutowiredFieldElement->>DefaultListableBeanFactory:resolveDependency(desc, beanName, autowiredBeanNames, typeConverter)<br>解析字段的依賴
DefaultListableBeanFactory->>DefaultListableBeanFactory:doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter)<br>解析指定的依賴關系
DefaultListableBeanFactory->>DefaultListableBeanFactory:findAutowireCandidates(beanName, type, descriptor)<br>查找符合自動裝配條件的候選 Bean
DefaultListableBeanFactory->>DefaultListableBeanFactory:addCandidateEntry(result, candidate, descriptor, requiredType)<br>向結果集中添加候選 Bean
DefaultListableBeanFactory->>AbstractBeanFactory:getType(name)<br>獲取指定 Bean 的類型
AbstractBeanFactory->>DefaultListableBeanFactory:返回被依賴Bean的類<br>返回依賴 Bean 的實際類
DefaultListableBeanFactory->>DependencyDescriptor:resolveCandidate(beanName, requiredType, beanFactory)<br>解析候選的依賴 Bean
DependencyDescriptor->>AbstractBeanFactory:getBean(name)<br>獲取指定的 Bean 實例
AbstractBeanFactory->>DependencyDescriptor:<br>返回具體的依賴 Bean 實例
DependencyDescriptor->>DefaultListableBeanFactory:<br>返回依賴的 Bean 實例給工廠
DefaultListableBeanFactory->>AutowiredFieldElement:<br>返回依賴的 Bean 給字段注入器
AutowiredFieldElement->>Field:field.set(bean, value)<br>實際設置 Bean 的字段值
七、源碼分析
前置條件
在Spring中,AutowiredAnnotationBeanPostProcessor是處理@Autowired等注解的關鍵類,它實現(xiàn)了下述兩個接口。因此,為了深入理解@Autowired的工作方式,研究這個類是非常有用的。簡而言之,為了完全理解@Autowired的工作機制,了解下述接口確實是必要的。這兩個接口提供了對bean生命周期中關鍵階段的干預,從而允許進行屬性注入和其他相關的操作。
-
MergedBeanDefinitionPostProcessor接口- 此接口提供的
postProcessMergedBeanDefinition方法允許后處理器修改合并后的bean定義。合并后的bean定義是一個已經考慮了所有父bean定義屬性的bean定義。對于@Autowired注解的處理,這一步通常涉及到收集需要被解析的@Autowired注解信息并準備對其進行后續(xù)處理。 - ?? MergedBeanDefinitionPostProcessor接口傳送門
- 此接口提供的
-
InstantiationAwareBeanPostProcessor接口- 此接口提供了幾個回調方法,允許后處理器在bean實例化之前和實例化之后介入bean的創(chuàng)建過程。特別是,
postProcessProperties方法允許后處理器對bean的屬性進行操作。對于@Autowired注解,這通常需要在屬性設置或依賴注入階段對 bean 進行處理,并將解析得到的值注入到bean中。 - ?? InstantiationAwareBeanPostProcessor接口傳送門
- 此接口提供了幾個回調方法,允許后處理器在bean實例化之前和實例化之后介入bean的創(chuàng)建過程。特別是,
收集階段
在org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition方法中,主要確保給定的bean定義與其預期的自動裝配元數據一致。
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
// 對于給定的bean名稱和類型,它首先嘗試查找相關的InjectionMetadata,這可能包含了該bean的字段和方法的注入信息
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
// 使用找到的InjectionMetadata來驗證bean定義中的配置成員是否與預期的注入元數據匹配。
metadata.checkConfigMembers(beanDefinition);
}
在org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata方法中,確保了始終為給定的bean名稱和類獲取最新和相關的InjectionMetadata,并利用緩存機制優(yōu)化性能。
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// 如果beanName為空,則使用類名作為緩存鍵。
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// 首先嘗試從并發(fā)緩存中獲取InjectionMetadata。
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);
}
// 為給定的類構建新的InjectionMetadata。
metadata = buildAutowiringMetadata(clazz);
// 將新構建的元數據更新到緩存中。
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
// 返回找到的或新構建的元數據。
return metadata;
}
在org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata方法中,查找類及其所有父類中的字段和方法,以找出所有帶有自動裝配注解的字段和方法,并為它們創(chuàng)建一個統(tǒng)一的InjectionMetadata對象。
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
// 檢查類是否含有自動裝配注解,若無則直接返回空的InjectionMetadata。
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<>();
// 處理類中的所有字段。
ReflectionUtils.doWithLocalFields(targetClass, field -> {
// 查找字段上的自動裝配注解。
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
if (ann != null) {
// ... [代碼部分省略以簡化]
boolean required = determineRequiredStatus(ann);
// 創(chuàng)建一個新的AutowiredFieldElement并加入到列表。
currElements.add(new AutowiredFieldElement(field, required));
}
});
// 處理類中的所有方法。
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
// 查找方法上的自動裝配注解。
MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
// ... [代碼部分省略以簡化]
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
// 創(chuàng)建一個新的AutowiredMethodElement并加入到列表。
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
// 將當前類的注入元素加入到總的注入元素列表的開頭。
elements.addAll(0, currElements);
// 處理父類。
targetClass = targetClass.getSuperclass();
}
// 循環(huán)直至Object類。
while (targetClass != null && targetClass != Object.class);
// 返回為元素列表創(chuàng)建的新的InjectionMetadata。
return InjectionMetadata.forElements(elements, clazz);
}
在org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#autowiredAnnotationTypes字段中,主要的用途是告訴AutowiredAnnotationBeanPostProcessor哪些注解它應該處理。當Spring容器解析bean定義并創(chuàng)建bean實例時,如果這個bean的字段、方法或構造函數上的注解被包含在這個autowiredAnnotationTypes集合中,那么AutowiredAnnotationBeanPostProcessor就會對它進行處理。
public AutowiredAnnotationBeanPostProcessor() {
this.autowiredAnnotationTypes.add(Autowired.class);
// ... [代碼部分省略以簡化]
}
注入階段
在org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties方法中,用于處理bean屬性的后處理,特別是通過@Autowired等注解進行的屬性注入。
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// 獲取與bean名稱和類相關的InjectionMetadata。
// 這包括該bean需要進行注入的所有字段和方法。
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
// 使用獲取到的InjectionMetadata,實際進行屬性的注入。
metadata.inject(bean, beanName, pvs);
}
// 如果在注入過程中出現(xiàn)BeanCreationException,直接拋出。
catch (BeanCreationException ex) {
throw ex;
}
// 捕獲其他異常,并以BeanCreationException的形式拋出,提供詳細的錯誤信息。
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
// 返回原始的PropertyValues,因為這個方法主要關注依賴注入而不是修改屬性。
return pvs;
}
在org.springframework.beans.factory.annotation.InjectionMetadata#inject方法中,主要目的是將所有需要注入的元素(例如帶有@Autowired等注解的字段或方法)注入到目標bean中。
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
// 獲取已經檢查的元素。通常,在初始化階段,所有的元素都會被檢查一次。
Collection<InjectedElement> checkedElements = this.checkedElements;
// 如果已經有檢查過的元素,則使用它們,否則使用所有注入的元素。
Collection<InjectedElement> elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements);
// 如果有需要注入的元素...
if (!elementsToIterate.isEmpty()) {
// 遍歷每個元素并注入到目標bean中。
for (InjectedElement element : elementsToIterate) {
// 對每個元素(字段或方法)執(zhí)行注入操作。
element.inject(target, beanName, pvs);
}
}
}
在org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject方法中,首先檢查字段的值是否已經被緩存。如果已緩存,則從緩存中獲取,否則重新解析。然后,它確保字段是可訪問的(特別是對于私有字段),并將解析的值設置到目標bean的相應字段中。
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
// 步驟1. 獲取代表帶有@Autowired注解的字段的Field對象。
Field field = (Field) this.member;
Object value;
// 步驟2. 如果字段的值已經被緩存(即先前已解析過),則嘗試從緩存中獲取。
if (this.cached) {
try {
// 從緩存中獲取已解析的字段值。
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
catch (NoSuchBeanDefinitionException ex) {
// 如果緩存中的bean已被意外刪除 -> 重新解析。
value = resolveFieldValue(field, bean, beanName);
}
}
else {
// 步驟3. 如果字段值未被緩存,直接解析。
value = resolveFieldValue(field, bean, beanName);
}
// 步驟4. 如果解析到的值不為null...
if (value != null) {
// 步驟4.1. 使字段可訪問,這是必要的,特別是當字段是private時。
ReflectionUtils.makeAccessible(field);
// 步驟4.2. 實際將解析的值注入到目標bean的字段中。
field.set(bean, value);
}
}
首先來到org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject方法中的步驟3。在org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue方法中,通過beanFactory.resolveDependency方法從Spring的bean工廠中解析字段的值。
@Nullable
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
// ... [代碼部分省略以簡化]
Object value;
try {
// 通過`beanFactory.resolveDependency`方法從Spring的bean工廠中解析字段的值
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
// ... [代碼部分省略以簡化]
return value;
}
在org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency方法中,首先嘗試獲取一個延遲解析代理。如果無法獲得,它會進一步嘗試解析依賴。doResolveDependency 是實際進行解析工作的方法。
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
// ... [代碼部分省略以簡化]
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
在org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency方法中,嘗試解析一個特定的依賴,首先查找所有可能的匹配的 bean,然后選擇一個最佳匹配的 bean。如果存在多個匹配的 bean 或沒有找到匹配的 bean,它會進行相應的處理。
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
// ... [代碼部分省略以簡化]
try {
// 如果存在快捷解決依賴的方法,使用它
Object shortcut = descriptor.resolveShortcut(this);
if (shortcut != null) {
return shortcut;
}
// 獲取依賴的類型
Class<?> type = descriptor.getDependencyType();
// ... [代碼部分省略以簡化]
// 步驟1. 根據依賴描述符查找匹配的bean
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
// 如果沒有找到匹配的bean
if (matchingBeans.isEmpty()) {
if (isRequired(descriptor)) {
// 如果依賴是必需的,拋出異常
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}
String autowiredBeanName;
Object instanceCandidate;
// 當找到多個匹配的bean
if (matchingBeans.size() > 1) {
// 確定最佳的自動裝配候選者
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
// 如果不能確定唯一的bean,嘗試解析不唯一的依賴
return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
}
else {
return null;
}
}
instanceCandidate = matchingBeans.get(autowiredBeanName);
}
else {
// 只找到一個匹配的bean
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
}
// 添加自動裝配的bean名到集合
if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
// 步驟2. 如果候選者是一個類,實例化它
if (instanceCandidate instanceof Class) {
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
Object result = instanceCandidate;
// ... [代碼部分省略以簡化]
return result;
}
// ... [代碼部分省略以簡化]
}
我們來到在org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency方法中的步驟1。在org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates方法中,首先基于給定的類型獲取所有可能的bean名。接著,對于每一個可能的候選bean,它檢查該bean是否是一個合適的自動注入候選,如果是,它將這個bean添加到結果集中。最后,方法返回找到的所有合適的候選bean。
protected Map<String, Object> findAutowireCandidates(
@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
// 根據所需的類型,包括所有父工廠中的bean,獲取所有可能的bean名
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this, requiredType, true, descriptor.isEager());
// ... [代碼部分省略以簡化]
// 遍歷所有候選bean名
for (String candidate : candidateNames) {
// 如果候選bean不是正在查找的bean本身并且它是一個合適的自動注入候選
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
// 添加這個候選bean到結果中
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
// ... [代碼部分省略以簡化]
// 返回找到的所有候選bean
return result;
}
在org.springframework.beans.factory.support.DefaultListableBeanFactory#addCandidateEntry方法中,主要獲取候選bean的類型,并將其添加到候選bean的集合中。
private void addCandidateEntry(Map<String, Object> candidates, String candidateName,
DependencyDescriptor descriptor, Class<?> requiredType) {
// ... [代碼部分省略以簡化]
candidates.put(candidateName, getType(candidateName));
}
在org.springframework.beans.factory.support.AbstractBeanFactory#getType(name)方法中,通過bean的名字來獲取對應bean的類型。
public Class<?> getType(String name) throws NoSuchBeanDefinitionException {
return getType(name, true);
}
我們來到在org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency方法中的步驟2。在org.springframework.beans.factory.config.DependencyDescriptor#resolveCandidate方法中,最后發(fā)現(xiàn)@Autowired 的整個流程最終還是從Spring容器中獲取一個bean實例并注入到相應的字段或構造函數參數中。
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
throws BeansException {
return beanFactory.getBean(beanName);
}
最后我們來到org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject方法中的步驟4.2。在 AutowiredFieldElement#inject 方法內部,通過resolveFieldValue(field, bean, beanName)方法,來確定了正確的bean值并滿足某個字段的 @Autowired 注解,將使用反射來實際設置這個值。具體地說,它會使用 Field 類的 set 方法來為目標對象的這個字段設置相應的值。這就是 @Autowired 在字段上使用時如何使得Spring能夠自動為這個字段注入值的背后原理。
// 步驟4. 如果解析到的值不為null...
if (value != null) {
// 步驟4.1. 使字段可訪問,這是必要的,特別是當字段是private時。
ReflectionUtils.makeAccessible(field);
// 步驟4.2. 實際將解析的值注入到目標bean的字段中。
field.set(bean, value);
}
八、注意事項
-
默認情況下,依賴是必需的
- 如果Spring找不到匹配的bean來注入,它會拋出一個異常。我們可以通過將
required屬性設置為false來使其變?yōu)榉潜匦瑁?code>@Autowired(required=false)。
- 如果Spring找不到匹配的bean來注入,它會拋出一個異常。我們可以通過將
-
類型匹配
- 默認情況下,Spring使用類型匹配來解析依賴。如果有多個匹配的bean,它會拋出一個異常。
-
使用
@Qualifier- 如果有多個相同類型的bean,我們可以使用
@Qualifier注解來指定bean的名稱,以解決歧義。
- 如果有多個相同類型的bean,我們可以使用
-
循環(huán)依賴
-
@Autowired可能導致循環(huán)依賴的問題。例如,A依賴于B,而B依賴于A。Spring有一定的機制來處理單例作用域的bean的循環(huán)依賴,但對于原型作用域的bean,循環(huán)依賴會導致異常。
-
-
注入位置
- 我們可以在字段、構造函數、或setter方法上使用
@Autowired。但是,推薦的做法是在構造函數上使用它,這樣可以確保所有的依賴在對象創(chuàng)建時都已經注入。
- 我們可以在字段、構造函數、或setter方法上使用
-
影響范圍
- 使用
@Autowired時,請注意不要在大范圍的bean(例如單例)中注入小范圍的bean(例如原型),除非我們清楚地知道自己在做什么。
- 使用
-
考慮使用構造函數注入
- 使用構造函數注入可以確保bean在構造時已完全初始化,從而使bean處于不變的狀態(tài)。這也有助于在單元測試中模擬依賴關系。
-
私有字段注入
- 盡管可以將
@Autowired應用于私有字段,但這意味著Spring通過反射繞過了正常的Java訪問控制來注入字段,這可能不是最佳實踐。
- 盡管可以將
-
不支持靜態(tài)字段
-
@Autowired不能用于靜態(tài)字段。這是因為靜態(tài)字段屬于類而不是實例,而Spring是通過實例進行依賴注入的。
-
-
不支持靜態(tài)方法
-
@Autowired也不能用于靜態(tài)setter方法或其他靜態(tài)方法
-
九、總結
最佳實踐總結
-
上下文初始化
- 當我們創(chuàng)建
AnnotationConfigApplicationContext并提供MyConfiguration類作為參數時,Spring 開始初始化上下文。這意味著它會加載所有的bean定義并準備創(chuàng)建實例。
- 當我們創(chuàng)建
-
組件掃描
- 在
MyConfiguration類中,我們使用了@ComponentScan注解指定了掃描的包路徑。這使得Spring掃描指定包和其子包中的所有類,并查找標記為@Component、@Service、@Repository和@Controller等注解的類。找到后,Spring 會自動將這些類注冊為bean。
- 在
-
依賴解析
- 在
MyController類中,我們在myService字段上使用了@Autowired注解。這告訴Spring,當創(chuàng)建MyControllerbean時,需要找到一個MyService類型的bean,并自動注入到該字段中。
- 在
-
實例化并注入
- 當我們從上下文中請求
MyController類型的bean時,Spring會先創(chuàng)建MyController的一個實例。但在此之前,它會查看所有帶有@Autowired注解的字段,然后為這些字段找到匹配的bean并注入。 - 在我們的例子中,Spring找到了
MyService類型的bean并將其注入到了myService字段中。
- 當我們從上下文中請求
-
執(zhí)行業(yè)務邏輯
- 在
showService方法被調用時,它簡單地打印了myService字段。由于這個字段已經被成功地自動注入,所以我們看到了預期的輸出,證明@Autowired功能正常。
- 在
-
結果
- 最終輸出顯示了
myService已經被成功地注入到MyController中,并顯示了其實例的內存地址。
- 最終輸出顯示了
源碼分析總結
-
核心后處理器
-
AutowiredAnnotationBeanPostProcessor是處理@Autowired等注解的主要后處理器。它實現(xiàn)了兩個關鍵的接口,MergedBeanDefinitionPostProcessor和InstantiationAwareBeanPostProcessor,這兩個接口允許在bean的生命周期中的關鍵階段進行干預,為屬性注入提供了機制。
-
-
收集階段
-
檢索Autowired的元數據
Spring首先使用
postProcessMergedBeanDefinition方法確保給定的bean定義與其預期的自動裝配元數據一致。在該方法中, Spring會嘗試查找與給定bean名稱和類型相關的
InjectionMetadata。這可能包括了該bean的字段和方法的注入信息。
-
尋找匹配的Autowiring元數據
- 在
findAutowiringMetadata中,Spring確保始終為給定的bean名稱和類獲取最新和相關的InjectionMetadata。Spring也利用了緩存機制,以提高性能。
- 在
-
構建Autowiring元數據
在
buildAutowiringMetadata方法中,Spring會查找類及其所有父類中的字段和方法,以找出所有帶有自動裝配注解的字段和方法。然后,為這些字段和方法創(chuàng)建一個統(tǒng)一的
InjectionMetadata對象。
-
檢查注解類型
- 在
AutowiredAnnotationBeanPostProcessor的構造方法中,主要的目的是告訴這個后處理器它應該處理哪些注解。例如,@Autowired就是這些注解之一。
- 在
-
-
注入階段
-
處理bean屬性的后處理
在
postProcessProperties中,Spring用于處理bean屬性的后處理,特別是通過@Autowired進行的屬性注入。這涉及到實際將解析得到的值注入到bean中。
-
注入元數據的實際注入操作
在
InjectionMetadata#inject方法中,這里會對bean進行屬性的實際注入。Spring會遍歷每一個需要注入的元素,并執(zhí)行實際的注入操作。
-
字段的實際注入
在
AutowiredFieldElement#inject中,Spring首先會檢查字段的值是否已經被緩存。如果已緩存,則從緩存中獲取,否則重新解析。然后,它確保字段是可訪問的,并將解析的值設置到目標bean的相應字段中。
-
解析依賴
在
doResolveDependency方法中,Spring開始嘗試解析一個特定的依賴。首先,基于給定的類型,Spring會查找所有匹配的bean。
如果找到多個匹配的bean,它會嘗試確定哪一個是最佳的自動裝配候選。
-
獲取bean的類型
在
addCandidateEntry方法中,Spring主要獲取候選bean的類型,并將其添加到候選bean的集合中。使用
getType方法,Spring可以通過bean的名字來獲取對應bean的類型。
-
從Spring容器中獲取bean實例
- 在
resolveCandidate中,即從Spring容器中獲取一個bean實例并注入到相應的字段或構造函數參數中。
- 在
-
反射注入
- 通過
field.set(bean, value)來完成實際字段注入的步驟,將解析出的bean實例(value)注入到目標bean的對應字段上。這是整個@Autowired流程的最終步驟
- 通過
-