SpringBoot實現(xiàn)ImportBeanDefinitionRegistrar動態(tài)注入
在閱讀Spring Boot源碼時,看到Spring Boot中大量使用ImportBeanDefinitionRegistrar來實現(xiàn)Bean的動態(tài)注入。它是Spring中一個強大的擴展接口。本篇文章來講講它相關使用。
Spring Boot中的使用
在Spring Boot 內置容器的相關自動配置中有一個ServletWebServerFactoryAutoConfiguration類。該類的部分代碼如下:
@Configuration(proxyBeanMethods = false) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @ConditionalOnClass(ServletRequest.class) @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(ServerProperties.class) @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration { // ... /** * Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via * {@link ImportBeanDefinitionRegistrar} for early registration. */ public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware { private ConfigurableListableBeanFactory beanFactory; // 實現(xiàn)BeanFactoryAware的方法,設置BeanFactory @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { if (beanFactory instanceof ConfigurableListableBeanFactory) { this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; } } // 注冊一個WebServerFactoryCustomizerBeanPostProcessor @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (this.beanFactory == null) { return; } registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class); registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class); } // 檢查并注冊Bean private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) { // 檢查指定類型的Bean name數(shù)組是否存在,如果不存在則創(chuàng)建Bean并注入到容器中 if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) { RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass); beanDefinition.setSynthetic(true); registry.registerBeanDefinition(name, beanDefinition); } } }
在這個自動配置類中,基本上展示了ImportBeanDefinitionRegistrar最核心的用法。這里該接口主要用來注冊BeanDefinition。
BeanPostProcessorsRegistrar實現(xiàn)了ImportBeanDefinitionRegistrar接口和BeanFactoryAware接口。其中BeanFactoryAware接口的實現(xiàn)是用來暴露Spring的ConfigurableListableBeanFactory對象。
而實現(xiàn)registerBeanDefinitions方法則是用來對Bean的動態(tài)注入,這里注入了WebServerFactoryCustomizerBeanPostProcessor和ErrorPageRegistrarBeanPostProcessor。
簡單了解了Spring Boot中的一個使用實例,下面我們總結一下使用方法,并自己實現(xiàn)一個類似的功能。
ImportBeanDefinitionRegistrar使用
Spring官方通過ImportBeanDefinitionRegistrar實現(xiàn)了@Component、@Service等注解的動態(tài)注入機制。
很多三方框架集成Spring的時候,都會通過該接口,實現(xiàn)掃描指定的類,然后注冊到spring容器中。 比如Mybatis中的Mapper接口,springCloud中的FeignClient接口,都是通過該接口實現(xiàn)的自定義注冊邏輯。
所有實現(xiàn)了該接口的類的都會被ConfigurationClassPostProcessor處理,ConfigurationClassPostProcessor實現(xiàn)了BeanFactoryPostProcessor接口,所以ImportBeanDefinitionRegistrar中動態(tài)注冊的bean是優(yōu)先于依賴其的bean初始化,也能被aop、validator等機制處理。
基本步驟:
- 實現(xiàn)ImportBeanDefinitionRegistrar接口;
- 通過registerBeanDefinitions實現(xiàn)具體的類初始化;
- 在@Configuration注解的配置類上使用@Import導入實現(xiàn)類;
簡單示例
這里實現(xiàn)一個非常簡單的操作,自定義一個@Mapper注解(并非Mybatis中的Mapper實現(xiàn)),實現(xiàn)類似@Component的功能,添加了@Mapper注解的類會被自動加載到spring容器中。
首先創(chuàng)建@Mapper注解。
@Documented @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) public @interface Mapper { }
創(chuàng)建UserMapper類,用于使用@Mapper注。
@Mapper public class UserMapper { }
定義ImportBeanDefinitionRegistrar的實現(xiàn)類MapperAutoConfigureRegistrar。如果需要獲取Spring中的一些數(shù)據,可實現(xiàn)一些Aware接口,這實現(xiàn)了ResourceLoaderAware。
public class MapperAutoConfigureRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware { private ResourceLoader resourceLoader; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { MapperBeanDefinitionScanner scanner = new MapperBeanDefinitionScanner(registry, false); scanner.setResourceLoader(resourceLoader); scanner.registerFilters(); scanner.addIncludeFilter(new AnnotationTypeFilter(Mapper.class)); scanner.doScan("com.secbro2.learn.mapper"); } @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } }
在上面代碼中,通過ResourceLoaderAware接口的setResourceLoader方法獲得到了ResourceLoader對象。
在registerBeanDefinitions方法中,借助ClassPathBeanDefinitionScanner類的實現(xiàn)類來掃描獲取需要注冊的Bean。
MapperBeanDefinitionScanner的實現(xiàn)如下:
public class MapperBeanDefinitionScanner extends ClassPathBeanDefinitionScanner { public MapperBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) { super(registry, useDefaultFilters); } protected void registerFilters() { addIncludeFilter(new AnnotationTypeFilter(Mapper.class)); } @Override protected Set<BeanDefinitionHolder> doScan(String... basePackages) { return super.doScan(basePackages); } }
MapperBeanDefinitionScanner
繼承子ClassPathBeanDefinitionScanner,掃描被@Mapper的注解的類。
在MapperBeanDefinitionScanner
中指定了addIncludeFilter方法的參數(shù)為包含Mapper的AnnotationTypeFilter。
當然也可以通過excludeFilters指定不加載的類型。這兩個方法由它們的父類ClassPathScanningCandidateComponentProvider提供的。
完成了上面的定義,則進行最后一步引入操作了。創(chuàng)建一個自動配置類MapperAutoConfig,并通過@Import引入自定義的Registrar。
@Configuration @Import(MapperAutoConfigureRegistrar.class) public class MapperAutoConfig { }
至此,整個代碼的功能已經編寫完成,下面寫一個單元測試。
@RunWith(SpringRunner.class) @SpringBootTest public class MapperAutoConfigureRegistrarTest { @Autowired UserMapper userMapper; @Test public void contextLoads() { System.out.println(userMapper.getClass()); } }
執(zhí)行單元測試代碼,會發(fā)現(xiàn)打印如下日志:
class com.edu.mapper.UserMapper
當然,這里的UserMapper并不是接口,這里的實現(xiàn)也并不是Mybatis中的實現(xiàn)形式。只是為了演示該功能的簡單示例。需要注意的是文中提到了兩種實現(xiàn)的實例,第一種是Spring Boot中的實現(xiàn),第二種是我們的Mapper實例。展現(xiàn)了兩種不同方法的注冊的操作,但整個使用流程是一致的,讀者注意仔細品味,并在此基礎上進行拓展更復雜的功能。
到此這篇關于SpringBoot實現(xiàn)ImportBeanDefinitionRegistrar動態(tài)注入的文章就介紹到這了,更多相關SpringBoot 動態(tài)注入內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
IntelliJ IDEA JRebel 安裝使用圖文教程(熱部署插件)
IDEA 全稱 IntelliJ IDEA,是java語言開發(fā)的集成環(huán)境,IntelliJ在業(yè)界被公認為最好的java開發(fā)工具之一。這篇文章主要介紹了IntelliJ IDEA 熱部署插件JRebel 安裝使用圖文教程,需要的朋友可以參考下2018-03-03spring源碼學習之bean的初始化以及循環(huán)引用
這篇文章主要給大家介紹了關于spring源碼學習之bean的初始化以及循環(huán)引用的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-10-10java.lang.UnsupportedOperationException分析及解決辦法
日常開發(fā)中我遇到java.lang.UnsupportedOperationException:異常兩次了,下面這篇文章主要給對大家介紹了關于java.lang.UnsupportedOperationException分析及解決辦法,需要的朋友可以參考下2024-03-03Spring Boot中單例類實現(xiàn)對象的注入方式
這篇文章主要介紹了Spring Boot中單例類實現(xiàn)對象的注入方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08