(转)Spring Boot通过ImportBeanDefinitionRegistrar动态注入Bean
转自:
Spring Boot通过ImportBeanDefinitionRegistrar动态注入Bean - 掘金在阅读SpringBoot源码时,看到SpringBoot中大量使用ImportBeanDefinitionRegistrar来实现Bean的动态注入。它是Spring中一个强大的扩展接口。本篇文章来https://juejin.cn/post/6844903971119693837
【README】
实现 ImportBeanDefinitionRegistrar 动态注入Bean的应用场景:
- 1.springboot封装多个同类型客户端的情况,如多个rabbitmq生产者客户端(多个集群),多个kafka生产者客户端(多个集群) 等等;springboot提供了对应的rabbittemplate,kafkatemplate等,但也需要多个连接;
- 2.该接口或该功能对于基于 springboot封装底层后台框架非常有用;
在阅读Spring Boot源码时,看到Spring Boot中大量使用ImportBeanDefinitionRegistrar来实现Bean的动态注入。它是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;// 实现BeanFactoryAware的方法,设置BeanFactory@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {if (beanFactory instanceof ConfigurableListableBeanFactory) {this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;}}// 注册一个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数组是否存在,如果不存在则创建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实现了ImportBeanDefinitionRegistrar接口和BeanFactoryAware接口。其中BeanFactoryAware接口的实现是用来暴露Spring的ConfigurableListableBeanFactory对象。
而实现registerBeanDefinitions方法则是用来对Bean的动态注入,这里注入了WebServerFactoryCustomizerBeanPostProcessor和ErrorPageRegistrarBeanPostProcessor。
简单了解了Spring Boot中的一个使用实例,下面我们总结一下使用方法,并自己实现一个类似的功能。
ImportBeanDefinitionRegistrar使用
Spring官方通过ImportBeanDefinitionRegistrar实现了@Component、@Service等注解的动态注入机制。
很多三方框架集成Spring的时候,都会通过该接口,实现扫描指定的类,然后注册到spring容器中。 比如Mybatis中的Mapper接口,springCloud中的FeignClient接口,都是通过该接口实现的自定义注册逻辑。
所有实现了该接口的类的都会被ConfigurationClassPostProcessor处理,ConfigurationClassPostProcessor实现了BeanFactoryPostProcessor接口,所以ImportBeanDefinitionRegistrar中动态注册的bean是优先于依赖其的bean初始化,也能被aop、validator等机制处理。
基本步骤:
- 实现ImportBeanDefinitionRegistrar接口;
- 通过registerBeanDefinitions实现具体的类初始化;
- 在@Configuration注解的配置类上使用@Import导入实现类;
简单示例
这里实现一个非常简单的操作,自定义一个@Mapper注解(并非Mybatis中的Mapper实现),实现类似@Component的功能,添加了@Mapper注解的类会被自动加载到spring容器中。
首先创建@Mapper注解。
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface Mapper {
}
创建UserMapper类,用于使用@Mapper注。
@Mapper
public class UserMapper {
}
定义ImportBeanDefinitionRegistrar的实现类MapperAutoConfigureRegistrar。如果需要获取Spring中的一些数据,可实现一些Aware接口,这实现了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类的实现类来扫描获取需要注册的Bean。
MapperBeanDefinitionScanner的实现如下:
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方法的参数为包含Mapper的AnnotationTypeFilter。
当然也可以通过excludeFilters指定不加载的类型。这两个方法由它们的父类ClassPathScanningCandidateComponentProvider提供的。
完成了上面的定义,则进行最后一步引入操作了。创建一个自动配置类MapperAutoConfig,并通过@Import引入自定义的Registrar。
@Configuration
@Import(MapperAutoConfigureRegistrar.class)
public class MapperAutoConfig {
}
至此,整个代码的功能已经编写完成,下面写一个单元测试。
@RunWith(SpringRunner.class)
@SpringBootTest
public class MapperAutoConfigureRegistrarTest {@AutowiredUserMapper userMapper;@Testpublic void contextLoads() {System.out.println(userMapper.getClass());}
}
执行单元测试代码,会发现打印如下日志:
class com.secbro2.learn.mapper.UserMapper
说明UserMapper已经被实例化成功,并注入Spring容器当中。
小结
当然,这里的UserMapper并不接口,这里的实现也并不是Mybatis中的实现形式。只是为了演示该功能的简单示例。需要注意的是文中提到了两种实现的实例,第一种是Spring Boot中的实现,第二种是我们的Mapper实例。展现了两种不同方法的注册的操作,但整个使用流程是一致的,读者注意仔细品味,并在此基础上进行拓展更复杂的功能。
原文链接:《Spring Boot通过ImportBeanDefinitionRegistrar动态注入Bean》
(转)Spring Boot通过ImportBeanDefinitionRegistrar动态注入Bean相关推荐
- springboot mapper无法注入_Spring Boot通过ImportBeanDefinitionRegistrar动态注入Bean
在阅读Spring Boot源码时,看到Spring Boot中大量使用ImportBeanDefinitionRegistrar来实现Bean的动态注入.它是Spring中一个强大的扩展接口.本篇文 ...
- Spring Boot轻松理解动态注入,删除bean
原文地址:http://412887952-qq-com.iteye.com/blog/2348445 我们通过getBean来获得对象,但这些对象都是事先定义好的,我们有时候要在程序中动态的加入 ...
- java手工注入bean_java相关:Spring中如何动态注入Bean实例教程
java相关:Spring中如何动态注入Bean实例教程 发布于 2020-3-8| 复制链接 摘记: 前言在Spring中提供了非常多的方式注入实例,但是由于在初始化顺序的不同,基于标注的注入方式, ...
- springboot使用ImportBeanDefinitionRegistrar 动态注册bean
[README] 1.采用 ImportBeanDefinitionRegistrar 动态注册bean,应用场景有: 如 一个后端服务需要用到多个 rabbitmq集群客户端,kafka客户端:这 ...
- Spring Boot:Consider defining a bean of type ‘*.*.*‘ in your configuration解决方案
Spring Boot:Consider defining a bean of type '*.*.*' in your configuration解决方案 参考文章: (1)Spring Boot: ...
- Spring Boot集成Quartz动态实现数据库任务
1. Quartz简介 1.1. 什么是Quartz Quartz是一个开源的任务调度框架.作用是基于定时.定期的策略来执行任务. 它是OpenSymphony开源组织在Job scheduling领 ...
- Spring : ImportBeanDefinitionRegistrar动态注入
1.美图 2.概述 广大开发者从Spring中开发了很多拓展接口使用方式,ImportBeanDefinitionRegistrar就是其中比较出名的一种,它能够加以利用去达到动态注册bean到spr ...
- Spring Boot + Mybatis 实现动态数据源
动态数据源 在很多具体应用场景的时候,我们需要用到动态数据源的情况,比如多租户的场景,系统登录时需要根据用户信息切换到用户对应的数据库.又比如业务A要访问A数据库,业务B要访问B数据库等,都可以使用动 ...
- Spring Boot 单例模式中依赖注入问题
在日常项目开发中,单例模式可以说是最常用到的设计模式,项目也常常在单例模式中需要使用 Service 逻辑层的方法来实现某些功能.通常可能会使用 @Resource 或者 @Autowired 来自动 ...
最新文章
- 汇编-Hello,world
- Redis常用命令之操作Set(集合)
- mysql数据库事务日志已满_服务器事务日志已满解决方法
- 无法解决 equal to 运算中 Chinese_PRC_CI_AS 和 SQL_Latin1_General_CP1_CI_AS 之间的排序规则冲突。...
- ArcMap 导入 wrl_flmic拍摄的素材如何无损导入电脑
- 目标检测 | RetinaNet:Focal Loss for Dense Object Detection
- mysql的增量备份方法_mySQL 增量备份方案(转)
- python︱写markdown一样写网页,代码快速生成web工具:streamlit 展示组件(三)
- Android实现手机和电脑屏幕共享
- php gd绘制图片,PHP-用GD绘制图形
- 如何查找共享计算机的用户名和密码错误,访问共享文件夹提示“未知的用户名或密码错误...
- webpack的source-map的详解
- 随记(七):Jboss漏洞检测利用工具
- MapReduce中名字的通俗解释--故事会
- QT案例实战1 - 从零开始编写一个OCR工具软件 (6) 关于QThread线程的使用
- 专业计算机术语中英文对照(一)
- android mp3 lrc歌词文件utf-8歌词显示为乱码,百度歌词显示乱码 LRC歌词批量转换 UTF-8编码批量转换为GB或ANSI 文本编码批量转换...
- vue 通过日期筛选数据
- 哈工大 计算机系统大作业 程序人生-Hello’s P2P From Program to Process
- java 左边补0_java补充0 java数字左侧补零补0 NumberFormat实现数字左侧补0
热门文章
- Codeforces Round #586 (Div. 1 + Div. 2) D. Alex and Julian 数学 + 思维
- Deltix Round, Spring 2021 E. Crypto Lights 组合数学 + 推公式
- King Gym - 102471H
- Codeforces Round #516 (Div. 2Div.1)
- cf375D. Tree and Queries
- CF1500C Matrix Sorting(拓扑排序)
- AT3860-[AGC020F]Arcs on a Circle【dp】
- P6805-[CEOI2020]春季大扫除【贪心,树链剖分,线段树】
- Loj#2769-「ROI 2017 Day 1」前往大都会【最短路树,斜率优化】
- P7717-「EZEC-10」序列【Trie】