Spring拓展點:BeanFactoryPostProcessor及其子接口

BeanFactoryPostProcessor

BeanFactoryPostProcessor是一個函數式接口,裏面只有一個方法:

@FunctionalInterface
public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

再看一下doc文檔上的相關描述:

Allows for custom modification of an application context's bean definitions, adapting the bean property values of the context's underlying bean factory. Application contexts can auto-detect BeanFactoryPostProcessor beans in their bean definitions and apply them before any other beans get created. A BeanFactoryPostProcessor may interact with and modify bean definitions, but never bean instances. Doing so may cause premature bean instantiation, violating the container and causing unintended side-effects. If bean instance interaction is required, consider implementing BeanPostProcessor instead.

機器翻譯:允許自定義修改應用程序上下文的bean定義,調整上下文的基礎bean工廠的bean屬性值。應用程序上下文可以在其bean定義中自動檢測BeanFactoryPostProcessor bean,並在創建任何其他bean之前先創建BeanFactoryPostProcessor。BeanFactoryPostProcessor可以與bean定義交互並修改bean定義,但絕不能與bean實例交互。這樣做可能會導致bean過早實例化,違反容器並導致意外的副作用。如果需要bean實例交互,請考慮實現BeanPostProcessor。實現該接口,可以允許我們的程序獲取到BeanFactory,從而修改BeanFactory,可以實現編程式的往Spring容器中添加Bean。

總結,也就是說,我們可以通過實現BeanFactoryPostProcessor接口,獲取BeanFactory,操作BeanFactory對象,修改BeanDefinition,但不要去實例化bean。

BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor的子類,在父類的基礎上,增加了新的方法,允許我們獲取到BeanDefinitionRegistry,從而編碼動態修改BeanDefinition。例如往BeanDefinition中添加一個新的BeanDefinition

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

Extension to the standard BeanFactoryPostProcessor SPI, allowing for the registration of further bean definitions before regular BeanFactoryPostProcessor detection kicks in. In particular, BeanDefinitionRegistryPostProcessor may register further bean definitions which in turn define BeanFactoryPostProcessor instances.

機器翻譯:對標準BeanFactoryPostProcessor SPI的擴展,允許在進行常規BeanFactoryPostProcessor檢測之前註冊其他Bean定義。特別是,BeanDefinitionRegistryPostProcessor可以註冊其他Bean定義,這些定義又定義了BeanFactoryPostProcessor實例。

執行時機

那麼BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessor接口是在什麼時候被回調的呢?

看過我之前文章的小夥伴不知道還記不得的,這兩個接口是在AbstractApplicationContext#refresh方法中執行到invokeBeanFactoryPostProcessors(beanFactory);方法時被執行的。Spring的編碼取名真的很藝術,方法名雖然很長,但是一看就知道在做什麼。

舉個例子

@Repository
public class OrderDao {

    public void query() {
        System.out.println("OrderDao query...");
    }
}
public class OrderService {

    private OrderDao orderDao;

    public void setDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }

    public void init() {
        System.out.println("OrderService init...");
    }

    public void query() {
        orderDao.query();
    }
}
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        //向Spring容器中註冊OrderService
        BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(OrderService.class)
                //這裏的屬性名是根據setter方法
                .addPropertyReference("dao", "orderDao")
                .setInitMethodName("init")
                .setScope(BeanDefinition.SCOPE_SINGLETON)
                .getBeanDefinition();

        registry.registerBeanDefinition("orderService", beanDefinition);

    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 在這裏修改orderService bean的scope爲PROTOTYPE
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("orderService");
        beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
    }
}

OrderService通過MyBeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry註冊到了容器中,又通過MyBeanDefinitionRegistryPostProcessor#postProcessBeanFactory方法修改了其Scope。最後運行主方法:

@Configuration
@ComponentScan
public class Main {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
        //能成功從容器中獲取orderService,併成功調用orderService.query();方法
        OrderService orderService = context.getBean(OrderService.class);
        orderService.query();
        OrderService orderService2 = context.getBean(OrderService.class);
        //false,orderService已經不是單例
        System.out.println(orderService == orderService2);
        context.close();
    }
}

典型應用:ConfigurationClassPostProcessor

在Spring中ConfigurationClassPostProcessor同時實現了BeanDefinitionRegistryPostProcessor接口和其父類接口中的方法。

  • ConfigurationClassPostProcessor#postProcessBeanFactory:主要負責對Full Configuration 配置進行增強,攔截@Bean方法來確保增強執行@Bean方法的語義。
  • ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry:負責掃描我們的程序,根據程序的中Bean創建BeanDefinition,並註冊到容器中。

歡迎各位關注公衆號:

Coder小黑

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章