目錄
一、概述
二、執行個體分析
三、源碼追蹤
四、總結
一、概述
【1】注解用法
根據@Autowired注解的源碼,可以看到該注解可以作用在構造器、參數、方法、屬性,都是從容器中擷取參數元件的值
-
- 标注在方法上:@Bean+方法參數,參數從容器中擷取,預設不寫@Autowired效果是一樣的,都能自動裝配
- 标注在構造器上:如果元件上隻有一個有參構造,這個有參構造的@Autowired可以省略,參數位置的元件還是可以自動從容器中擷取
- 标注在參數位置
- 标注在屬性位置
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
相關注解:
- @Autowired:預設按類型裝配,如果我們想使用按名稱裝配,可以結合@Qualifier注解一起使用(Spring提供)
- @Qualifier():指定裝配的bean,多個執行個體時可以結合@Autowired一起使用
- @Primary:自動裝配時當出現多個bean候選者時,被注解為
的bean将作為首選者
@Primary
- @Resource:預設按名稱裝配,當找不到與名稱比對的bean才會按類型裝配(不支援
和
@Primary
功能,JDK提供)
@Autowired(required = false)
- @Inject:需要導入javax.inject的包,和Autowired功能一樣,但是沒有
功能(JDK提供)
required=false
【2】自動裝配
Spring利用依賴注入(DI)完成對IOC容器中各個元件的依賴關系指派
@Autowired
自動注入(Spring提供的):
-
- 預設優先按照去容器中找對應的元件:applicationContext.getBean()
- 如果找到多個相同類型的元件,再将屬性的名稱作為元件的ID去容器中查找
- @Qualifier()注解:該注解指定需要裝配的元件ID,而不是使用屬性名
- 自動裝配預設必須要對屬性指派,沒有就會報錯,可以使用
指定非必須就不會報錯@Autowired(required = false)
- @Primary注解:自動裝配時當出現多個bean候選者時,被注解為
的bean将作為首選者,否則将抛出異常,如果使用了@Primary
指定裝配的bean,則還是使用明确指定裝配的bean@Qualifier()
@Resource(JSR250)和@Inject(JSR330)(JDK提供的)
@Resource:
-
- 預設按照元件名稱進行裝配,也可以指定名稱進行裝配
- 當找不到與名稱比對的bean會按類型裝配
- 不支援
和@Primary
功能@Autowired(required = false)
- 如果同時指定了name和type,則從Spring上下文中找到唯一比對的bean進行裝配,找不到則抛出異常。
- 如果指定了name,則從上下文中查找名稱(id)比對的bean進行裝配,找不到則抛出異常。
- 如果指定了type,則從上下文中找到類似比對的唯一bean進行裝配,找不到或是找到多個,都會抛出異常。
- 如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有比對,則回退為一個原始類型進行比對,如果比對則自動裝配。
@Inject:
- 需要導入javax.inject的包,和Autowired功能一樣,但是沒有
功能required=false
【3】@Autowired和@Resource注解的差別
-
- @Autowired由Spring提供,隻按照byType注入;@Resource由J2EE提供,預設按照byName自動注入,當找不到與名稱比對的bean會按類型裝配
- @Autowired預設按類型裝配,預設情況下必須要求依賴對象存在,如果要允許null值,可以設定它的required屬性為false。如果想使用名稱裝配可以結合@Qualifier注解進行使用。
- @Resource,預設按照名稱進行裝配,名稱可以通過name屬性進行指定,如果沒有指定name屬性,當注解寫在字段上時,預設取字段名進行名稱查找。如果注解寫在setter方法上預設取屬性名進行裝配。當找不到與名稱比對的bean時才按照類型進行裝配。但是需要注意的是,如果name屬性一旦指定,就隻會按照名稱進行裝配。
二、執行個體分析
這裡隻對@Autowired注解标注在屬性位置進行執行個體分析
【1】@Autowired注解
// 啟動類
@Test
public void TestMain() {
// 建立IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = applicationContext.getBean(UserService.class);
System.out.println("userService:" + userService);
}
// Service
@Service
public class UserService {
@Autowired(required = false) // 指定非必須
@Qualifier("userDao2") // 指定裝配bean
private UserDao userDao;
@Override
public String toString() {
return "UserService{" +
"userDao=" + userDao +
'}';
}
}
// Dao
@Repository
public class UserDao {
private String label = "1";
public void setLabel(String label) {
this.label = label;
}
@Override
public String toString() {
return "UserDao{" +
"label='" + label + '\'' +
'}';
}
}
// 配置類
@Configuration
@ComponentScan({"dao","service","controller"})
public class AppConfig {
@Primary // 首選裝配bean
@Bean("userDao2")
public UserDao userDao(){
UserDao userDao = new UserDao();
userDao.setLabel("2");
return userDao;
}
}
輸出結果如下,由于上面使用
@Qualifier("userDao2")
指定了要裝配的bean,是以這裡輸出的是label=’2‘:
- 如果将
改為
@Qualifier("userDao2")
,則裝配的是
@Qualifier("userDao")
label=’1‘
【2】@Resource注解
@Service
public class UserService {
@Resource(name = "userDao2",type = UserDao.class)
private UserDao userDao;
@Override
public String toString() {
return "UserService{" +
"userDao=" + userDao +
'}';
}
}
- 預設按照元件名稱進行裝配,也可以指定名稱進行裝配
- 當找不到與名稱比對的bean會按類型裝配
- 不支援
和
@Primary
功能
@Autowired(required = false)
- 如果同時指定了name和type,則從Spring上下文中找到唯一比對的bean進行裝配,找不到則抛出異常。
- 如果指定了name,則從上下文中查找名稱(id)比對的bean進行裝配,找不到則抛出異常。
- 如果指定了type,則從上下文中找到類似比對的唯一bean進行裝配,找不到或是找到多個,都會抛出異常。
- 如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有比對,則回退為一個原始類型進行比對,如果比對則自動裝配。
三、源碼追蹤
這裡對@Autowired注解底層進行源碼分析
參考:
@Autowired是用來裝配bean的,肯定和bean的執行個體化有關,先經過了refresh方法,在finishBeanFactoryInitialization方法中getBean,然後走getObject的時候觸發bean的初始化。bean的初始化是一個很複雜地方,在AbstractAutowireCapableBeanFactory#doCreateBean方法中,先建立一個BeanWrapper,它的内部成員變量wrappedObject中存放的就是執行個體化的MyService對象,Spring Bean的生命周期源碼詳解 - 【Spring底層原理】,再往後進入populateBean方法進行屬性注入
Spring對autowire注解的實作邏輯位于類:
AutowiredAnnotationBeanPostProcessor#postProcessProperties
之中,——>findAutowiringMetadata——>buildAutowiringMetadata,核心代碼就在buildAutowiringMetadata方法裡面
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
} else {
List<InjectedElement> elements = new ArrayList();
// 需要處理的目标類
Class targetClass = clazz;
do {
List<InjectedElement> currElements = new ArrayList();
// 通過反射擷取該類所有的字段,并周遊每一個字段,并通過方法findAutowiredAnnotation周遊每一個字段的所用注解,并如果用autowired修飾了,則傳回auotowired相關屬性
ReflectionUtils.doWithLocalFields(targetClass, (field) -> {
MergedAnnotation<?> ann = this.findAutowiredAnnotation(field);
if (ann != null) {
// 校驗autowired注解是否用在了static方法上
if (Modifier.isStatic(field.getModifiers())) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
// 判斷是否指定了required
boolean required = this.determineRequiredStatus(ann);
currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement(field, required));
}
});
// 和上面一樣的邏輯,但是是通過反射處理類的method
ReflectionUtils.doWithLocalMethods(targetClass, (method) -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
MergedAnnotation<?> ann = this.findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterCount() == 0 && this.logger.isInfoEnabled()) {
this.logger.info("Autowired annotation should only be used on methods with parameters: " + method);
}
boolean required = this.determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement(method, required, pd));
}
}
});
// 用@Autowired修飾的注解可能不止一個,是以都加在currElements這個容器裡面,一起處理
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
} while(targetClass != null && targetClass != Object.class);
return InjectionMetadata.forElements(elements, clazz);
}
}
- 擷取需要處理的目标類
- 通過doWithLocalFields方法傳入目标類參數,通過反射擷取該類所有的字段,并周遊每一個字段,并通過方法findAutowiredAnnotation周遊每一個字段的所用注解,并如果用autowired修飾了,則傳回auotowired相關屬性
- 判斷autowired注解是否用在了static方法上
- 如有多個@Autowired修飾的注解,都加在currElements這個容器裡面,一起處理
最後傳回包含所有帶有autowire注解修飾的一個InjectionMetadata集合,如下
-
- targetClass:要處理的目标類
- elements:上述方法擷取到的是以elements集合
public InjectionMetadata(Class<?> targetClass, Collection<InjectionMetadata.InjectedElement> elements) {
this.targetClass = targetClass;
this.injectedElements = elements;
}
有了目标類,與所有需要注入的元素集合之後,我們就可以實作autowired的依賴注入邏輯了,實作的方法如下:
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {
if (!this.validatedBeanNames.contains(beanName)) {
if (!this.shouldSkip(this.beanFactory, beanName)) {
List<String> invalidProperties = new ArrayList();
PropertyDescriptor[] var6 = pds;
int var7 = pds.length;
for(int var8 = 0; var8 < var7; ++var8) {
PropertyDescriptor pd = var6[var8];
if (this.isRequiredProperty(pd) && !pvs.contains(pd.getName())) {
invalidProperties.add(pd.getName());
}
}
if (!invalidProperties.isEmpty()) {
throw new BeanInitializationException(this.buildExceptionMessage(invalidProperties, beanName));
}
}
this.validatedBeanNames.add(beanName);
}
return pvs;
}
調用InjectionMetadata中定義的inject方法:
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Collection<InjectionMetadata.InjectedElement> checkedElements = this.checkedElements;
Collection<InjectionMetadata.InjectedElement> elementsToIterate = checkedElements != null ? checkedElements : this.injectedElements;
if (!((Collection)elementsToIterate).isEmpty()) {
Iterator var6 = ((Collection)elementsToIterate).iterator();
while(var6.hasNext()) {
InjectionMetadata.InjectedElement element = (InjectionMetadata.InjectedElement)var6.next();
element.inject(target, beanName, pvs);
}
}
}
進行周遊,然後調用inject方法,inject方法其實作邏輯如下:
protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs) throws Throwable {
if (this.isField) {
Field field = (Field)this.member;
// 暴力破解的方法,通過反射技術對對象進行執行個體化和指派
ReflectionUtils.makeAccessible(field);
field.set(target, this.getResourceToInject(target, requestingBeanName));
} else {
if (this.checkPropertySkipping(pvs)) {
return;
}
try {
Method method = (Method)this.member;
ReflectionUtils.makeAccessible(method);
// 注入的bean的名字,這個方法的功能就是根據這個bean的名字去拿到它
method.invoke(target, this.getResourceToInject(target, requestingBeanName));
} catch (InvocationTargetException var5) {
throw var5.getTargetException();
}
}
}
- 使用了反射技術,分成字段和方法去處理的。
- makeAccessible這樣的可以稱之為暴力破解的方法,通過反射技術對對象進行執行個體化和指派
- getResourceToInject方法的參數就是要注入的bean的名字,這個方法的功能就是根據這個bean的名字去拿到它
四、總結
@AutoWired自動注入過程圖: