我們會經常使用@Autowired、@Resource、@Value 這些注解給屬性指派。
那麼這幾個注解是怎麼給屬性指派的呢?還有其他的方法可以給屬性指派嗎?
使用@Bean注解+set方法,設定依賴注入方式(byName,byType)。
配置類
bean
測試
上面幾張圖:在配置類中使用@Bean 注解并設定注入方式, 配置bean。
這種方式在spring中已經被定義為過期的方法,但是還能用,隻是spring不建議使用。
person類的屬性上并沒有加@Autowired,但是也能注入成功。
上圖這段代碼就是記錄這種注入方式的代碼,記住是記錄,意思是說:隻要用這種方式注入bean的spring都會在這個地方記錄,就是将這些bean的名字存到一個集合中,友善後續注入。
PropertyDescriptor[] pds = bw.getPropertyDescriptors();
PropertyDescriptor:屬性描述器, 是javaBean 裡自帶的。在這個屬性描述器裡會記錄屬性的名字,屬性的set 方法 ,get方法,等等。
前提是必須要有set方法或是get方法,如果一個類裡隻定義了屬性,沒有set方法或則是getf方法,那麼也是拿不到這個屬性的。
PropertyDescriptor中的name屬性是通過解析set方法擷取的。例如:setOr123 , 那麼name屬性的值就是 or123。
spring 在拿到所有屬性後進行循環周遊
for (PropertyDescriptor pd : pds) {
if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&
!BeanUtils.isSimpleProperty(pd.getPropertyType())) {
result.add(pd.getName());
}
}
- pd.getWriteMethod() != null : 将沒有set方法的屬性給過濾掉。
- !pvs.contains(pd.getName()) : 将在MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition方法中指派過的屬性過濾掉。
- !BeanUtils.isSimpleProperty(pd.getPropertyType()): 将簡單類型的屬性過濾掉。
上圖這個方法的最後面,就是下面這段代碼
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
将記錄屬性進行指派。
@Autowired,@Value,@Resource給屬性指派
上圖中的1是處理 @Autowired,@Value ,2是處理@Resource。
以下主要看是AutowiredAnnotationBeanPostProcessor 這個實作類。
上圖中可以看到 AutowiredAnnotationBeanPostProcessor 實作了SmartInstantiationAwareBeanPostProcessor 和 MergedBeanDefinitionPostProcessor 這兩個接口。
MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition尋找注入點
根據類找到注入點
注入點:加了@Autowired 的屬性或者方法。
ReflectionUtils.doWithLocalFields(targetClass, field -> {
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
if (ann != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
});
上述代碼就是拿到類的所有屬性,然後循環判斷屬性上面有沒有加注解(@Autowired或@Value隻要有一個注解就行)如果有注解且不是靜态屬性,就會加入到緩存中 currElements。
1.靜态的屬性屬于類的,不依賴于某個對象,也可以稱為類屬性。不同的對象都可以通路到類的屬性。帶參數的構造方法中不能包含靜态的屬性。 它的生命随着類的消亡而消亡。
2.非靜态屬性隻能屬于某個對象,其它的對象不能通路它的屬性。随着對象的消亡而消亡。
boolean required = determineRequiredStatus(ann);
這行代碼的作用 就是處理 required 的,如果等于true 就是必須要給該屬性指派,如果沒有值就會報異常。
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))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
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));
}
});
上述這段代碼是處理方法的,和上面的屬性大緻差不多。
唯一不同的是:
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
這個就是處理橋接方法,有興趣的可以搜一下。
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
}
這段代碼就是判斷這個bean 的類型。如果是String等基礎資料類型則不需要尋找注入點,就會直接傳回。
SmartInstantiationAwareBeanPostProcessor.postProcessProperties 主要是處理屬性
在拿到注入點後就會執行這個方法,這個方法中會找到注入點的值,并給注入點指派。
标号1的地方是找值,找到值後,就會執行标号2 ,給注入點指派。