Spring擴充套件介面(2):BeanDefinitionRegistryPostProcessor

2023-10-10 12:01:47

在此係列文章中,我總結了Spring幾乎所有的擴充套件介面,以及各個擴充套件點的使用場景。並整理出一個bean在spring中從被載入到最終初始化的所有可延伸點的順序呼叫圖。這樣,我們也可以看到bean是如何一步步載入到spring容器中的。


BeanDefinitionRegistryPostProcessor

1、概述

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry var1) throws BeansException;
}

BeanDefinitionRegistryPostProcessor為容器級後置處理器。容器級的後置處理器會在Spring容器初始化後、重新整理前執行一次。還有一類為Bean級後置處理器,在每一個Bean範例化前後都會執行。

通常,BeanDefinitionRegistryPostProcessor用於在bean解析後範例化之前通過BeanDefinitionRegistry對BeanDefintion進行增刪改查。

常見如mybatis的Mapper介面注入就是實現的此介面。

2、簡單案例

下面是一個範例,展示瞭如何實現動態的給spring容器新增一個Bean:

public class User {
    String name;
    String password;
}


import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

@Component
public class DynamicBeanRegistration implements BeanDefinitionRegistryPostProcessor, Ordered {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanRegistry) throws BeansException {
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClass(User.class);
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        PropertyValue propertyValue1 = new PropertyValue("name", "張三");
        PropertyValue propertyValue2 = new PropertyValue("password", "123456");
        propertyValues.addPropertyValue(propertyValue1);
        propertyValues.addPropertyValue(propertyValue2);
        beanDefinition.setPropertyValues(propertyValues);
        beanRegistry.registerBeanDefinition("user", beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("user");
        System.out.println(beanDefinition.getBeanClassName());
        User user = beanFactory.getBean(User.class);
        System.out.println(user.getName());
        System.out.println(user.getPassword());
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

輸出:

com.sandy.springex.beanfefinitionregistrypostprocessor.User
張三
123456
  • 首先定義了一個名為"User"的Java類,包含了兩個屬性:name和password。
  • 然後定義了一個名為"DynamicBeanRegistration"的元件(通過@Component註解),實現了BeanDefinitionRegistryPostProcessor介面和Ordered介面。
  • 在postProcessBeanDefinitionRegistry方法中,建立了一個RootBeanDefinition物件,並設定其beanClass為User類。接著建立了一個MutablePropertyValues物件,並通過PropertyValue物件設定了name和password屬性的值。最後,將propertyValues設定到beanDefinition中,並使用beanRegistry註冊了一個名為"user"的BeanDefinition。
  • 在postProcessBeanFactory方法中,通過beanFactory獲取了名為"user"的BeanDefinition,並輸出了其beanClassName。然後使用beanFactory獲取了一個User物件,並輸出了其name和password屬性的值。

該程式碼通過實現BeanDefinitionRegistryPostProcessor介面,在Spring容器啟動時動態註冊了一個名為"user"的Bean,並設定了其name和password屬性的值。在後續的BeanFactory初始化過程中,可以通過beanFactory獲取到該動態註冊的Bean,並存取其屬性值。

當容器中有多個BeanDefinitionRegistryPostProcessor的時候,可以通過實現Ordered介面來指定順序:

@Override
public int getOrder() {
   return 0; //值越小,優先順序越高
}

3、原始碼分析

  • 在DynamicBeanRegistration打上斷點,啟動SpringApplication,可以看到左下角的呼叫鏈路。

  • 紅框中5步都是在springboot中進行,最後super.refresh()是呼叫大家熟悉的spring的AbstractApplicationContext的refresh方法。

  • 繼續向下看


  • 接下來進入核心的invokeBeanFactoryPostProcessors方法,大概邏輯是先取出所有實現了BeanDefinitionRegistryPostProcessor介面的類,然後優先呼叫實現了PriorityOrdered介面的元件,再呼叫實現了Ordered介面的元件。

  • 最後,遍歷呼叫BeanDefinitionRegistryPostProcessor元件postProcessBeanDefinitionRegistry方法