javascript
(转)Spring Boot通过ImportBeanDefinitionRegistrar动态注入Bean
轉(zhuǎn)自:
Spring Boot通過ImportBeanDefinitionRegistrar動態(tài)注入Bean - 掘金在閱讀SpringBoot源碼時(shí),看到SpringBoot中大量使用ImportBeanDefinitionRegistrar來實(shí)現(xiàn)Bean的動態(tài)注入。它是Spring中一個(gè)強(qiáng)大的擴(kuò)展接口。本篇文章來https://juejin.cn/post/6844903971119693837
【README】
實(shí)現(xiàn) ImportBeanDefinitionRegistrar 動態(tài)注入Bean的應(yīng)用場景:
- 1.springboot封裝多個(gè)同類型客戶端的情況,如多個(gè)rabbitmq生產(chǎn)者客戶端(多個(gè)集群),多個(gè)kafka生產(chǎn)者客戶端(多個(gè)集群) 等等;springboot提供了對應(yīng)的rabbittemplate,kafkatemplate等,但也需要多個(gè)連接;
- 2.該接口或該功能對于基于 springboot封裝底層后臺框架非常有用;
在閱讀Spring Boot源碼時(shí),看到Spring Boot中大量使用ImportBeanDefinitionRegistrar來實(shí)現(xiàn)Bean的動態(tài)注入。它是Spring中一個(gè)強(qiáng)大的擴(kuò)展接口。本篇文章來講講它相關(guān)使用。
Spring Boot中的使用
在Spring Boot 內(nèi)置容器的相關(guān)自動配置中有一個(gè)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;// 實(shí)現(xiàn)BeanFactoryAware的方法,設(shè)置BeanFactory@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {if (beanFactory instanceof ConfigurableListableBeanFactory) {this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;}}// 注冊一個(gè)WebServerFactoryCustomizerBeanPostProcessor@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {if (this.beanFactory == null) {return;}registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",WebServerFactoryCustomizerBeanPostProcessor.class);registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",ErrorPageRegistrarBeanPostProcessor.class);}// 檢查并注冊Beanprivate 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);}}} }在這個(gè)自動配置類中,基本上展示了ImportBeanDefinitionRegistrar最核心的用法。這里該接口主要用來注冊BeanDefinition。
BeanPostProcessorsRegistrar實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口和BeanFactoryAware接口。其中BeanFactoryAware接口的實(shí)現(xiàn)是用來暴露Spring的ConfigurableListableBeanFactory對象。
而實(shí)現(xiàn)registerBeanDefinitions方法則是用來對Bean的動態(tài)注入,這里注入了WebServerFactoryCustomizerBeanPostProcessor和ErrorPageRegistrarBeanPostProcessor。
簡單了解了Spring Boot中的一個(gè)使用實(shí)例,下面我們總結(jié)一下使用方法,并自己實(shí)現(xiàn)一個(gè)類似的功能。
ImportBeanDefinitionRegistrar使用
Spring官方通過ImportBeanDefinitionRegistrar實(shí)現(xiàn)了@Component、@Service等注解的動態(tài)注入機(jī)制。
很多三方框架集成Spring的時(shí)候,都會通過該接口,實(shí)現(xiàn)掃描指定的類,然后注冊到spring容器中。 比如Mybatis中的Mapper接口,springCloud中的FeignClient接口,都是通過該接口實(shí)現(xiàn)的自定義注冊邏輯。
所有實(shí)現(xiàn)了該接口的類的都會被ConfigurationClassPostProcessor處理,ConfigurationClassPostProcessor實(shí)現(xiàn)了BeanFactoryPostProcessor接口,所以ImportBeanDefinitionRegistrar中動態(tài)注冊的bean是優(yōu)先于依賴其的bean初始化,也能被aop、validator等機(jī)制處理。
基本步驟:
- 實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口;
- 通過registerBeanDefinitions實(shí)現(xiàn)具體的類初始化;
- 在@Configuration注解的配置類上使用@Import導(dǎo)入實(shí)現(xiàn)類;
簡單示例
這里實(shí)現(xiàn)一個(gè)非常簡單的操作,自定義一個(gè)@Mapper注解(并非Mybatis中的Mapper實(shí)現(xiàn)),實(shí)現(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的實(shí)現(xiàn)類MapperAutoConfigureRegistrar。如果需要獲取Spring中的一些數(shù)據(jù),可實(shí)現(xiàn)一些Aware接口,這實(shí)現(xiàn)了ResourceLoaderAware。
public class MapperAutoConfigureRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {private ResourceLoader resourceLoader;@Overridepublic 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");}@Overridepublic void setResourceLoader(ResourceLoader resourceLoader) {this.resourceLoader = resourceLoader;} }在上面代碼中,通過ResourceLoaderAware接口的setResourceLoader方法獲得到了ResourceLoader對象。
在registerBeanDefinitions方法中,借助ClassPathBeanDefinitionScanner類的實(shí)現(xiàn)類來掃描獲取需要注冊的Bean。
MapperBeanDefinitionScanner的實(shí)現(xiàn)如下:
public class MapperBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {public MapperBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {super(registry, useDefaultFilters);}protected void registerFilters() {addIncludeFilter(new AnnotationTypeFilter(Mapper.class));}@Overrideprotected Set<BeanDefinitionHolder> doScan(String... basePackages) {return super.doScan(basePackages);} }MapperBeanDefinitionScanner繼承子ClassPathBeanDefinitionScanner,掃描被@Mapper的注解的類。
在MapperBeanDefinitionScanner中指定了addIncludeFilter方法的參數(shù)為包含Mapper的AnnotationTypeFilter。
當(dāng)然也可以通過excludeFilters指定不加載的類型。這兩個(gè)方法由它們的父類ClassPathScanningCandidateComponentProvider提供的。
完成了上面的定義,則進(jìn)行最后一步引入操作了。創(chuàng)建一個(gè)自動配置類MapperAutoConfig,并通過@Import引入自定義的Registrar。
?
至此,整個(gè)代碼的功能已經(jīng)編寫完成,下面寫一個(gè)單元測試。
@RunWith(SpringRunner.class) @SpringBootTest public class MapperAutoConfigureRegistrarTest {@AutowiredUserMapper userMapper;@Testpublic void contextLoads() {System.out.println(userMapper.getClass());} }執(zhí)行單元測試代碼,會發(fā)現(xiàn)打印如下日志:
class com.secbro2.learn.mapper.UserMapper說明UserMapper已經(jīng)被實(shí)例化成功,并注入Spring容器當(dāng)中。
小結(jié)
當(dāng)然,這里的UserMapper并不接口,這里的實(shí)現(xiàn)也并不是Mybatis中的實(shí)現(xiàn)形式。只是為了演示該功能的簡單示例。需要注意的是文中提到了兩種實(shí)現(xiàn)的實(shí)例,第一種是Spring Boot中的實(shí)現(xiàn),第二種是我們的Mapper實(shí)例。展現(xiàn)了兩種不同方法的注冊的操作,但整個(gè)使用流程是一致的,讀者注意仔細(xì)品味,并在此基礎(chǔ)上進(jìn)行拓展更復(fù)雜的功能。
原文鏈接:《Spring Boot通過ImportBeanDefinitionRegistrar動態(tài)注入Bean》
?
總結(jié)
以上是生活随笔為你收集整理的(转)Spring Boot通过ImportBeanDefinitionRegistrar动态注入Bean的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一款可以定位到国外的虚拟定位工具一款可以
- 下一篇: docker 打包镜像_Spring B