天天看點

重識Spring的BeanPostProcessor處理器重識Spring的BeanPostProcessor處理器

重識Spring的BeanPostProcessor處理器

BeanPostProcessor

是Spring用來實作快速拓展最為核心的接口,現在常說的AOP就是通過實作BeanPostProcessor接口來進行拓展。

下面就先看下BeanPostProcessor到底長什麼樣?🧐

BeanPostProcessor接口

public interface BeanPostProcessor {

	 //在bean init之前執行
   @Nullable
   default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
      return bean;
   }

   // 在bean執行完init方法之後執行
   @Nullable
   default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
      return bean;
   }

}
           

BeanPostProcessor中就兩個方法,postProcessBeforeInitialization和postProcessAfterInitialization,分别會在Spring執行invokeInitMethods方法(包含了afterPropertiesSet和自定義init方法)前後進行執行。

這裡要特别注意BeanPostProcessor中方法的最後一個單詞是Initialization,這個很容易和postProcessBeforeInstantiation/postProcessAfterInstantiation方法混淆,兩者的調用時機是不一樣的,Initialization是在初始化前後,而Instantiation是在執行個體化前後。

下面介紹下BeanPostProcessor用途比較重要的兩個實作:CommonAnnotationBeanPostProcessor和AutowiredAnnotationBeanPostProcessor。

Spring會在添加<context:component-scan > XML配置以及@ComponentScan()注解時建立預設的CommonAnnotationBeanPostProcessor和AutowiredAnnotationBeanPostProcessor處理器。

CommonAnnotationBeanPostProcessor

重識Spring的BeanPostProcessor處理器重識Spring的BeanPostProcessor處理器

最上層實作就是

BeanPostProcessor

接口。

CommonAnnotationBeanPostProcessor

支援常見的Java注解,特别是JSR-250注解,是以大部分的注解都會關于“資源”的建構、銷毀和使用。

因為實作了InitDestroyAnnotationBeanPostProcessor接口,是以支援

@PostConstruct

@PreDestroy

關于對象初始化和銷毀的注解。

支援注解如下:

  • @Resource
  • @PostConstruct
  • @PreDestroy
  • @WebServiceRef
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

   /**
    * 在bean執行個體化之前調用
    */
   @Nullable
   default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
      return null;
   }

   /**
    * 在bean執行個體化之後調用
    */
   default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
      return true;
   }

   /**
    * 當使用注解的時候,通過這個方法來完成屬性的注入
    */
   @Nullable
   default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
         throws BeansException {

      return null;
   }

   /**
    * 屬性注入後執行的方法,在5.1版本被廢棄
    */
   @Deprecated
   @Nullable
   default PropertyValues postProcessPropertyValues(
         PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {

      return pvs;
   }

}
           

如何解析

CommonAnnotationBeanPostProcessor

類中有如下方法會在Bean初始化後執行,用來填充BeanDefinition中的初始化方法以及@Resource對應的字段定義;

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
   // 處理@PostConstruct和@PreDestroy注解
   super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
   // 處理@Resouce注解對應的屬性以及方法
   InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
   metadata.checkConfigMembers(beanDefinition);
}
           

postProcessMergedBeanDefinition

實際調用的是父類

InitDestroyAnnotationBeanPostProcessor

中的方法;

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
   // 擷取生命周期方法
   LifecycleMetadata metadata = findLifecycleMetadata(beanType);
   // 驗證相關方法
   metadata.checkConfigMembers(beanDefinition);
}
           

findLifecycleMetadata方法中會調用**buildLifecycleMetadata()**方法來建構對應生命周期方法的LifecycleElement(這裡主要是@PostConstruct或者@PreDestroy注解對應的方法)。

private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
   if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {
      return this.emptyLifecycleMetadata;
   }

   // @PostConstruct方法
   List<LifecycleElement> initMethods = new ArrayList<>();
   // @PreDestroy方法
   List<LifecycleElement> destroyMethods = new ArrayList<>();
  
   Class<?> targetClass = clazz;

   do {
      final List<LifecycleElement> currInitMethods = new ArrayList<>();
      final List<LifecycleElement> currDestroyMethods = new ArrayList<>();
      // 周遊目前類以及父類所有方法
      ReflectionUtils.doWithLocalMethods(targetClass, method -> {
         // 目前方法的注解中包含initAnnotationType注解時(@PostConstruct)
         if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
            // 如果有,把它封裝成LifecycleElement對象,存儲起來
            LifecycleElement element = new LifecycleElement(method);
            // 将建立好的元素添加到集合中
            currInitMethods.add(element);
            if (logger.isTraceEnabled()) {
               logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
            }
         }
         // 目前方法的注解中包含destroyAnnotationType注解(PreDestroy)
         if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
            // 如果有,把它封裝成LifecycleElement對象,存儲起來
            currDestroyMethods.add(new LifecycleElement(method));
            if (logger.isTraceEnabled()) {
               logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
            }
         }
      });

      initMethods.addAll(0, currInitMethods);
      destroyMethods.addAll(currDestroyMethods);
      // 擷取父類class對象
      targetClass = targetClass.getSuperclass();
   }
   while (targetClass != null && targetClass != Object.class);

   return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
         new LifecycleMetadata(clazz, initMethods, destroyMethods));
}
           

buildLifecycleMetadata

方法會封裝類中對應生命周期方法的中繼資料,通過

getDeclaredMethods()

擷取類中的所有方法,然後判斷是否使用了**@PostConstruct或者@PreDestroy**注解,是就會以

LifecycleMetadata

的形式放入到

lifecycleMetadataCache

中,并且一層一層擷取父類相關内容。

findResourceMetadata

方法同樣會調用

buildResourceMetadata

方法來建構@Resource注解對應的屬性或方法的ResourceElement。

也就是存在如下資料存放方式:

InjectionMetadata -> Collection injectedElements -> ResourceElement

LifecycleMetadata -> Collection initMethods / destroyMethods -> LifecycleElement

private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
   // 判斷目前的類是否是Resource或javax.xml.ws.WebServiceRef類
   if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) {
      return InjectionMetadata.EMPTY;
   }

   List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
   Class<?> targetClass = clazz;

   do {
      final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

      // @Resource屬性注入
      // 查詢是否有webService,ejb,Resource的屬性注解,但是不支援靜态屬性
      ReflectionUtils.doWithLocalFields(targetClass, field -> {
         if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
            if (Modifier.isStatic(field.getModifiers())) {
               throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
            }
            currElements.add(new WebServiceRefElement(field, field, null));
         }
         else if (ejbClass != null && field.isAnnotationPresent(ejbClass)) {
            if (Modifier.isStatic(field.getModifiers())) {
               throw new IllegalStateException("@EJB annotation is not supported on static fields");
            }
            currElements.add(new EjbRefElement(field, field, null));
         }
         else if (field.isAnnotationPresent(Resource.class)) {
            //注意靜态字段不支援
            if (Modifier.isStatic(field.getModifiers())) {
               throw new IllegalStateException("@Resource annotation is not supported on static fields");
            }
            //如果不想注入某一類型對象 可以将其加入ignoredResourceTypes中
            if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
               //字段會封裝到ResourceElement
               currElements.add(new ResourceElement(field, field, null));
            }
         }
      });

      // @Resource方法注入
      ReflectionUtils.doWithLocalMethods(targetClass, method -> {
        
         Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
         if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
            return;
         }
         //如果重寫了父類的方法,則使用子類的
         if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
            if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
               // 靜态字段不支援
               if (Modifier.isStatic(method.getModifiers())) {
                  throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
               }
               if (method.getParameterCount() != 1) {
                  throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
               }
               PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
               currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
            }
            else if (ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass)) {
               if (Modifier.isStatic(method.getModifiers())) {
                  throw new IllegalStateException("@EJB annotation is not supported on static methods");
               }
               if (method.getParameterCount() != 1) {
                  throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
               }
               PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
               currElements.add(new EjbRefElement(method, bridgedMethod, pd));
            }
            else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
               // 不支援靜态方法
               if (Modifier.isStatic(method.getModifiers())) {
                  throw new IllegalStateException("@Resource annotation is not supported on static methods");
               }
               Class<?>[] paramTypes = method.getParameterTypes();
               if (paramTypes.length != 1) {
                  throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
               }
               if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
                  PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                  currElements.add(new ResourceElement(method, bridgedMethod, pd));
               }
            }
         }
      });
     
      // 父類的都放在第一位,是以父類是最先完成依賴注入的
      elements.addAll(0, currElements);
      targetClass = targetClass.getSuperclass();
   }
   while (targetClass != null && targetClass != Object.class);

   return InjectionMetadata.forElements(elements, clazz);
}
           

buildResourceMetadata

方法和

buildLifecycleMetadata

實作基本一樣,隻不過是加了字段的周遊以及最後生成的中繼資料不一樣罷了。

PS:

這裡擷取類字段和方法的函數,Spring也是進行了充分的優化;

private static Field[] getDeclaredFields(Class<?> clazz) {

   Assert.notNull(clazz, "Class must not be null");

   Field[] result = declaredFieldsCache.get(clazz);

   if (result == null) {
      try {
         // 擷取clazz所有定義的屬性
         result = clazz.getDeclaredFields();
         // EMPTY_FIELD_ARRAY和result在沒有字段情況下都是空數組,是以用一個固定的空數組來表示,減少新對象的記憶體占用
         declaredFieldsCache.put(clazz, (result.length == 0 ? EMPTY_FIELD_ARRAY : result));
      }
      catch (Throwable ex) {
         throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() +
               "] from ClassLoader [" + clazz.getClassLoader() + "]", ex);
      }
   }
   // 傳回clazz的屬性數組
   return result;
}
           

首先,Spring将所有對應的方法或字段都放入到了Map緩存中。

此外,下面這行代碼;

declaredFieldsCache.put(clazz, (result.length == 0 ? EMPTY_FIELD_ARRAY : result));

在緩存中還沒有對應類的字段或方法時,通過Class類的

getDeclaredFields

方法先擷取到,然後放入map集合中,主要是這裡會對擷取的數組長度做個判斷,當為0時,實際放入的是EMPTY_FIELD_ARRAY對象,而EMPTY_FIELD_ARRAY定義如下;

EMPTY_FIELD_ARRAY實際就是一個長度為0的數組,實際上EMPTY_FIELD_ARRAY和類字段或方法不存在時代表的含義是相同的,而getDeclaredFields和getDeclaredMethods方法每次都會傳回一個新數組,是以Spring利用EMPTY_FIELD_ARRAY作了代替,來讓result數組較早的回收,就是為了減少記憶體的消耗。

AutowiredAnnotationBeanPostProcessor

重識Spring的BeanPostProcessor處理器重識Spring的BeanPostProcessor處理器

AutowiredAnnotationBeanPostProcessor從類名的定義上就能看出,該處理器主要适合**@Autowired**注解相關的。該處理器和CommonAnnotationBeanPostProcessor處理器執行的時間都是一樣的,隻是會後于CommonAnnotationBeanPostProcessor處理器執行。

這裡隻介紹相關的幾個重要方法。

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
   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<>();

      // 周遊類中的每個屬性,判斷屬性是否包含指定的屬性(通過 findAutowiredAnnotation 方法)
      // 如果存在則儲存,這裡注意,屬性儲存的類型是 AutowiredFieldElement
      ReflectionUtils.doWithLocalFields(targetClass, field -> {
         MergedAnnotation<?> ann = findAutowiredAnnotation(field);
         if (ann != null) {
            //Autowired注解不支援靜态方法
            if (Modifier.isStatic(field.getModifiers())) {
               if (logger.isInfoEnabled()) {
                  logger.info("Autowired annotation is not supported on static fields: " + field);
               }
               return;
            }
            //檢視是否是required的
            boolean required = determineRequiredStatus(ann);
            currElements.add(new AutowiredFieldElement(field, required));
         }
      });


      // 周遊類中的每個方法,判斷屬性是否包含指定的屬性(通過 findAutowiredAnnotation 方法)
      // 如果存在則儲存,這裡注意,方法儲存的類型是 AutowiredMethodElement
      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);
            // AutowiredMethodElement裡封裝了一個PropertyDescriptor(比字段多了一個參數)
            currElements.add(new AutowiredMethodElement(method, required, pd));
         }
      });

      // 父類的都放在第一位,是以父類是最先完成依賴注入的
      elements.addAll(0, currElements);
      targetClass = targetClass.getSuperclass();
   }
   while (targetClass != null && targetClass != Object.class);

   // InjectionMetadata就是對clazz和elements的一個包裝而已
   return InjectionMetadata.forElements(elements, clazz);
}
           

@Autowired處理器和@Resource處理器相比,方法類似,都是擷取到所有的字段或者方法,然後周遊進行相應的判斷處理。@Autowired處理器相對而言,包裝的類型發生了變化(AutowiredMethodElement,AutowiredMethodElement),并且,在@Autowired下,所有對象的注入都是從父類開始的,也就是說在同注解的屬性注入時,父類的屬性注入會先于子類的屬性注入。

兩個處理器在populateBean對象注入的時候,執行的邏輯都是如下所示:

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
   // 這裡擷取到的是之前解析儲存的Metadata
   InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
   try {
      metadata.inject(bean, beanName, pvs);
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
   }
   return pvs;
}
           

findResourceMetadata

從之前解析類時所儲存在BeanPostProcessor中的

injectionMetadataCache

Map集合中擷取到InjectionMetadata對象,然後執行inject方法,循環周遊注入。

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()) {
      for (InjectedElement element : elementsToIterate) {
         if (logger.isTraceEnabled()) {
            logger.trace("Processing injected element of bean '" + beanName + "': " + element);
         }
         element.inject(target, beanName, pvs);
      }
   }
}
           

拓展:BeanPostProcessor執行順序

  • 實作了PriorityOrdered接口 > Ordered接口 > 兩者都沒實作;
  • 實作了MergedBeanDefinitionPostProcessor一定最後執行;
  • 相同情況下,還是按照PriorityOrdered > Ordered相對順序。

相同情況下比較如下:

private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {
   // 判斷o1是否實作了PriorityOrdered接口
   boolean p1 = (o1 instanceof PriorityOrdered);
   // 判斷o2是否實作了PriorityOrdered接口
   boolean p2 = (o2 instanceof PriorityOrdered);
   // 如果o1實作了PriorityOrdered接口,o2沒有,則o1排前面
   if (p1 && !p2) {
      return -1;
   }
   // 如果o2實作了PriorityOrdered接口,而o1沒有,o2排前面
   else if (p2 && !p1) {
      return 1;
   }
   // 如果o1和o2都實作或者都沒有實作PriorityOrdered接口
   // 拿到o1的order值,如果沒有實作Ordered接口,值為Ordered.LOWEST_PRECEDENCE
   int i1 = getOrder(o1, sourceProvider);
   // 拿到o2的order值,如果沒有實作Ordered接口,值為Ordered.LOWEST_PRECEDENCE
   int i2 = getOrder(o2, sourceProvider);
   // 通過order值排序(order值越小,優先級越高)
   return Integer.compare(i1, i2);
}
           

繼續閱讀