天天看點

spring依賴注入、屬性填充(上)

作者:XL1024

我們會經常使用@Autowired、@Resource、@Value 這些注解給屬性指派。

那麼這幾個注解是怎麼給屬性指派的呢?還有其他的方法可以給屬性指派嗎?

使用@Bean注解+set方法,設定依賴注入方式(byName,byType)。

spring依賴注入、屬性填充(上)

配置類

spring依賴注入、屬性填充(上)

bean

spring依賴注入、屬性填充(上)

測試

上面幾張圖:在配置類中使用@Bean 注解并設定注入方式, 配置bean。

這種方式在spring中已經被定義為過期的方法,但是還能用,隻是spring不建議使用。

person類的屬性上并沒有加@Autowired,但是也能注入成功。

spring依賴注入、屬性填充(上)

上圖這段代碼就是記錄這種注入方式的代碼,記住是記錄,意思是說:隻要用這種方式注入bean的spring都會在這個地方記錄,就是将這些bean的名字存到一個集合中,友善後續注入。

spring依賴注入、屬性填充(上)
spring依賴注入、屬性填充(上)
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());
   }
}           
  1. pd.getWriteMethod() != null : 将沒有set方法的屬性給過濾掉。
  2. !pvs.contains(pd.getName()) : 将在MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition方法中指派過的屬性過濾掉。
  3. !BeanUtils.isSimpleProperty(pd.getPropertyType()): 将簡單類型的屬性過濾掉。
spring依賴注入、屬性填充(上)

上圖這個方法的最後面,就是下面這段代碼

if (pvs != null) {
   applyPropertyValues(beanName, mbd, bw, pvs);
}           

将記錄屬性進行指派。

@Autowired,@Value,@Resource給屬性指派

spring依賴注入、屬性填充(上)
spring依賴注入、屬性填充(上)

上圖中的1是處理 @Autowired,@Value ,2是處理@Resource。

以下主要看是AutowiredAnnotationBeanPostProcessor 這個實作類。

spring依賴注入、屬性填充(上)

上圖中可以看到 AutowiredAnnotationBeanPostProcessor 實作了SmartInstantiationAwareBeanPostProcessor 和 MergedBeanDefinitionPostProcessor 這兩個接口。

MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition尋找注入點

spring依賴注入、屬性填充(上)
spring依賴注入、屬性填充(上)

根據類找到注入點

注入點:加了@Autowired 的屬性或者方法。

spring依賴注入、屬性填充(上)
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.非靜态屬性隻能屬于某個對象,其它的對象不能通路它的屬性。随着對象的消亡而消亡。

spring依賴注入、屬性填充(上)

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 主要是處理屬性

spring依賴注入、屬性填充(上)

在拿到注入點後就會執行這個方法,這個方法中會找到注入點的值,并給注入點指派。

spring依賴注入、屬性填充(上)
spring依賴注入、屬性填充(上)
spring依賴注入、屬性填充(上)

标号1的地方是找值,找到值後,就會執行标号2 ,給注入點指派。