1. 程式人生 > >【Spring註解驅動開發】在@Import註解中使用ImportBeanDefinitionRegistrar向容器中註冊bean

【Spring註解驅動開發】在@Import註解中使用ImportBeanDefinitionRegistrar向容器中註冊bean

## 寫在前面 > 在前面的文章中,我們學習瞭如何使用@Import註解向Spring容器中匯入bean,可以使用@Import註解快速向容器中匯入bean,小夥伴們可以參見《[【Spring註解驅動開發】使用@Import註解給容器中快速匯入一個元件](https://mp.weixin.qq.com/s?__biz=Mzg3MzE1NTIzNA==&mid=2247484863&idx=1&sn=faca9edb10665d357089a290220ede2f&chksm=cee51a72f992936430364b018e07f062c2cb4bbe7111d0b615a1937215170976e5caf23a227b&token=1611686244&lang=zh_CN#rd)》。可以在@Import註解中使用ImportSelector介面匯入bean,小夥伴們可以參見《[【Spring註解驅動開發】在@Import註解中使用ImportSelector介面匯入bean](https://mp.weixin.qq.com/s?__biz=Mzg3MzE1NTIzNA==&mid=2247484870&idx=1&sn=a371224a8c2b9f70a41ff88976d6b0e6&chksm=cee51a0bf992931d3e39ddf70061ac8de713c817ec6561075a740eb18c7269ce66d50459dd58&token=1611686244&lang=zh_CN#rd)》一文。今天,我們就來說說,如何在@Import註解中使用ImportBeanDefinitionRegistrar向容器中註冊bean。 > > 專案工程原始碼已經提交到GitHub:[https://github.com/sunshinelyz/spring-annotation](https://github.com/sunshinelyz/spring-annotation) ## ImportBeanDefinitionRegistrar概述 ### 概述 我們先來看看ImportBeanDefinitionRegistrar是個什麼鬼,點選進入ImportBeanDefinitionRegistrar原始碼,如下所示。 ```java package org.springframework.context.annotation; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.core.type.AnnotationMetadata; public interface ImportBeanDefinitionRegistrar { default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) { registerBeanDefinitions(importingClassMetadata, registry); } default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { } } ``` 由原始碼可以看出,ImportBeanDefinitionRegistrar本質上是一個介面。在ImportBeanDefinitionRegistrar介面中,有一個registerBeanDefinitions()方法,通過registerBeanDefinitions()方法,我們可以向Spring容器中註冊bean例項。 Spring官方在動態註冊bean時,大部分套路其實是使用ImportBeanDefinitionRegistrar介面。 所有實現了該介面的類都會被ConfigurationClassPostProcessor處理,ConfigurationClassPostProcessor實現了BeanFactoryPostProcessor介面,所以ImportBeanDefinitionRegistrar中動態註冊的bean是優先於依賴其的bean初始化的,也能被aop、validator等機制處理。 ### 使用方法 ImportBeanDefinitionRegistrar需要配合@Configuration和@Import註解,@Configuration定義Java格式的Spring配置檔案,@Import註解匯入實現了ImportBeanDefinitionRegistrar介面的類。 ## ImportBeanDefinitionRegistrar例項 既然ImportBeanDefinitionRegistrar是一個介面,那我們就建立一個MyImportBeanDefinitionRegistrar類,實現ImportBeanDefinitionRegistrar介面,如下所示。 ```java package io.mykit.spring.plugins.register.condition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; /** * @author binghe * @version 1.0.0 * @description ImportBeanDefinitionRegistrar的實現類 */ public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * AnnotationMetadata: 當前類的註解資訊 * BeanDefinitionRegistry:BeanDefinition註冊類 * 通過呼叫BeanDefinitionRegistry介面的registerBeanDefinition()方法,可以將所有需要新增到容器中的bean注入到容器中。 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry){ } } ``` 可以看到,這裡,我們先建立了MyImportBeanDefinitionRegistrar類的大體框架。接下來,我們在PersonConfig2類上的@Import註解中,新增MyImportBeanDefinitionRegistrar類,如下所示。 ```java @Configuration @Import({Department.class, Employee.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class}) public class PersonConfig2 { ``` 接下來,建立一個Company類,作為測試測試ImportBeanDefinitionRegistrar介面的bean,如下所示。 ```java package io.mykit.spring.plugins.register.bean; /** * @author binghe * @version 1.0.0 * @description 測試ImportBeanDefinitionRegistrar介面的使用 */ public class Company { } ``` 接下來,就要實現MyImportBeanDefinitionRegistrar類中的registerBeanDefinitions()方法的邏輯了,新增邏輯後的registerBeanDefinitions()方法如下所示。 ```java /** * AnnotationMetadata: 當前類的註解資訊 * BeanDefinitionRegistry:BeanDefinition註冊類 * 通過呼叫BeanDefinitionRegistry介面的registerBeanDefinition()方法,可以將所有需要新增到容器中的bean注入到容器中。 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry){ boolean employee = registry.containsBeanDefinition("employee"); boolean department = registry.containsBeanDefinition("department"); if (employee && department){ BeanDefinition beanDefinition = new RootBeanDefinition(Company.class); registry.registerBeanDefinition("company", beanDefinition); } } ``` registerBeanDefinitions()方法的實現邏輯很簡單,就是判斷Spring容器中是否同時存在以employee命名的bean和以department命名的bean,如果同時存在以employee命名的bean和以department命名的bean,則向Spring容器中注入一個以company命名的bean。 接下來,我們就執行SpringBeanTest類中的testAnnotationConfig7()方法來進行測試,輸出結果資訊如下所示。 ```bash org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory personConfig2 io.mykit.spring.plugins.register.bean.Department io.mykit.spring.plugins.register.bean.Employee io.mykit.spring.plugins.register.bean.User io.mykit.spring.plugins.register.bean.Role person binghe001 ``` 可以看到,在輸出結果中,並沒有看到“company”,這是因為輸出結果中存在io.mykit.spring.plugins.register.bean.Department和io.mykit.spring.plugins.register.bean.Employee,並不存在我們程式碼邏輯中的department和employee。所以,我們將registerBeanDefinitions()方法的邏輯稍微修改下,修改後的程式碼如下所示。 ```java /** * AnnotationMetadata: 當前類的註解資訊 * BeanDefinitionRegistry:BeanDefinition註冊類 * 通過呼叫BeanDefinitionRegistry介面的registerBeanDefinition()方法,可以將所有需要新增到容器中的bean注入到容器中。 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry){ boolean employee = registry.containsBeanDefinition(Employee.class.getName()); boolean department = registry.containsBeanDefinition(Department.class.getName()); if (employee && department){ BeanDefinition beanDefinition = new RootBeanDefinition(Company.class); registry.registerBeanDefinition("company", beanDefinition); } } ``` 接下來,我們再次執行SpringBeanTest類中的testAnnotationConfig7()方法來進行測試,輸出結果資訊如下所示。 ```bash org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory personConfig2 io.mykit.spring.plugins.register.bean.Department io.mykit.spring.plugins.register.bean.Employee io.mykit.spring.plugins.register.bean.User io.mykit.spring.plugins.register.bean.Role person binghe001 company ``` 可以看到,此時輸出了company,說明Spring容器中已經成功註冊了以company命名的bean。 **好了,咱們今天就聊到這兒吧!別忘了給個在看和轉發,讓更多的人看到,一起學習一起進步!!** > 專案工程原始碼已經提交到GitHub:[https://github.com/sunshinelyz/spring-annotation](https://github.com/sunshinelyz/spring-annotation) ## 寫在最後 > 如果覺得文章對你有點幫助,請微信搜尋並關注「 冰河技術 」微信公眾號,跟冰河學習Spring註解驅動開發。公眾號回覆“spring註解”關鍵字,領取Spring註解驅動開發核心知識圖,讓Spring註解驅動開發不再