spring:实现初始化动态bean|获取对象型数组配置文件

0. 引言

近期因为要完成实现中间件的工具包组件,其中涉及要读取对象型的数组配置文件,并且还要将其加载为bean,因为使用了spring 4.3.25.RELEASE版本,很多springboot的相关特性无法支持,因此特此记录,以方便后续同学有相同情况可以参考

1. 获取对象型数组配置文件

首先对象型数组配置文件如下所示:

minio.clients[0].name=xxx
minio.clients[0].endpoint=http://ip1:9000
minio.clients[0].access.key=admin
minio.clients[0].secret.key=asd
minio.clients[0].default.bucket=wu
minio.clients[1].name=yyy
minio.clients[1].endpoint=http://ip2:9000
minio.clients[1].access.key=admin
minio.clients[1].secret.key=asd
minio.clients[1].default.bucket=wu

转换成yml的格式如下:

minio:clients:- name: xxxendpoint: http://ip1:9000access:key: adminsecret:key: asddefault:bucket: wu- name: yyyendpoint: http://ip2:9000access:key: adminsecret:key: asddefault:bucket: wu

如果是springboot项目,我们直接用一个@ConfigurationProperties(prefix="minio.clients"),然后配置一个实体类就可以实现了,但这里因为遇到的是较老版本的spring项目,不支持该注解。于是尝试用其他方式实现。

@Value形式
首先来看@Value的确能够帮助我们读取到配置项,但是只能针对基础类型或者基础类型数组的配置项,对于我们对象项的数组配置文件,就不支持了,而spring中,除了这种方式,还有可以直接通过操作Environment对象来实现

Environment形式
可以看到通过environment.getProperty方法,是可以获取到我们想要的配置项的,于是这种方式明显是可行的。

同时Environment读取配置项时,要指定配置文件,于是需要借助@PropertySource来声明,同时因为这是一个工具包,也就是说配置文件可能会没有,没有则不用初始化bean,有对应配置文件再自动初始化bean,这是我们想要实现的

点开@PropertySource注解,我们可以看到一个ignoreResourceNotFound属性,从属性名已经告诉我们它的作用了,将其值设置为true,就可以实现配置文件存在时读取配置项,不存在时也不会报错

在这里插入图片描述

完整的示例如下:

@Configuration
@PropertySource(value = {"classpath:applicaiton.properties","classpath:minio.properties"}, ignoreResourceNotFound=true)
public class MinioMultiConfiguration {@Resourceprivate Environment environment;...   
}    

如何动态获取非固定长度数组配置项?
其次我们要观察这里的需求,因为要获取的minio.clients配置实际上是多个,这里我们只是列举了两个,因为实现的是工具包,后续可能还会配置很多个,所以长度是非预期的。

那么我们就需要获取到这个数组的长度,如果是springboot,可以直接通过List<配置实体类> list定义的集合,获取集合长度即可,但是这里spring中,无法直接获取到数组长度,于是为了满足需求,只能采取了一个笨方法,直接定义一个长度配置minio.clients.length=2,后续大家这里有更好的办法可以留言讨论

然后我们就在代码中通过for循环获取配置项即可

2. 如何将bean注册到spring容器

同时因为我这里的需求还需要初始化对应的MinioClient,那就需要将创建的bean,注册到spring容器中,而注册到容器除了@Bean注解的方式,如下所示

@Configuration
public class MinioConfiguration {/*** 对象存储服务的url*/@Value("${minio.endpoint:null}")private String endpoint;/*** 用户ID*/@Value("${minio.access.key:null}")private String accessKey;/*** 账户密码*/@Value("${minio.secret.key:null}")private String secretKey;/*** 默认桶名*/@Value("${minio.default.bucket:null}")private String defaultBucketName = "wu";@Beanpublic MinioClient minioClient() throws Exception{MinioProperties minioProperties = new MinioProperties(endpoint, accessKey, secretKey, defaultBucketName);if(StringUtils.isEmpty(minioProperties.getEndpoint())){return null;}return new MinioClient(minioProperties.getEndpoint(), minioProperties.getAccessKey(), minioProperties.getSecretKey());}
}

还可以通过BeanFactory来进行注册,如下所示

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("beanName", new MinioClient());

可以看到我们这里因为要循环读取配置项,bean的个数是不定的,所以固定使用@Bean的形式肯定行不通,只能通过beanFactory进行注册

bean的注册时机
我们知道bean肯定是要在项目启动时就注册的,但是启动时也分了很多阶段,比如我们初始化好的bean实际上是要通过@Autowired@Resource引用的,所以我们肯定需要在这两个引用之前就注册好,否则就会报错bean找不到了。

spring中项目启动时执行方法,有几种方式,比较常用的有@PostConstruct注解的方式,但这种方式的执行顺序是@Bean > @Autowired > @PostConstruct,因此它肯定是不行了

于是我们尝试另一种方式通过申明BeanFactoryPostProcessor接口,实现postProcessBeanFactory方法,这可以对beanFactory进行一些自定义的修改,而我们就可以在这些修改中将bean注册进去

同时因为要通过Environment获取配置项,于是我们还需要申明下EnvironmentAware,通过setEnvironment方法把Environment注册进来,当然你也可以选择通过beanFactory.getBean("environment")获取

完整的示例代码如下:

public class MinioMultiBeanFactoryPostProcessor implements BeanFactoryPostProcessor, EnvironmentAware {private final static Logger log = LogManager.getLogger(MinioMultiBeanFactoryPostProcessor.class);private Environment environment;@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {if(!environment.containsProperty("minio.clients.length")){log.error("未识别到minio.clients.length,取消配置多个minioClient");return;}Integer length = 0;try {length = environment.getProperty("minio.clients.length", Integer.class);}catch (Exception e){throw new RuntimeException("minioClient初始化失败,minio.clients.length数据类型为Int");}for (int i = 0; length != null && i < length; i++) {String name = environment.getProperty("minio.clients["+i+"].name");String endpoint = environment.getProperty("minio.clients["+i+"].endpoint");String access = environment.getProperty("minio.clients["+i+"].access.key");String secret = environment.getProperty("minio.clients["+i+"].secret.key");String bucket = environment.getProperty("minio.clients["+i+"].default.bucket");try{// 自定义对象MinioProperties minioProperties = new MinioProperties(endpoint, access, secret, bucket);// 创建clientMinioClient minioClient = new MinioClient(minioProperties.getEndpoint(), minioProperties.getAccessKey(), minioProperties.getSecretKey());beanFactory.registerSingleton(name+"MinioClient", minioClient);}catch (Exception e){log.error(String.format("minioClient初始化失败:%s", ExceptionUtil.getErrorInfo(e)));}}}
}

然后还需要结合上述说明的@Bean > @Autowired的顺序,因为我们自定义的BeanFactoryPostProcessor现在还只是个单纯的类,我们也需要将其声明为bean才能实现修改BeanFactory的目的,于是通过@Bean来初始化

@Configuration
@PropertySource(value = {"classpath:application.properties","classpath:minio.properties"}, ignoreResourceNotFound=true)
public class MinioMultiConfiguration {@Beanpublic MinioMultiBeanFactoryPostProcessor minioMultiBeanFactoryPostProcessor(){return new MinioMultiBeanFactoryPostProcessor();}}

至此,我们初始化动态bean的操作就完成了,以上方式适用于任何spring项目,对需要搭建中间包的项目更加适用,大家可以选择性参考

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/115497.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ffmpeg抠图

1.不用png&#xff0c;用AVFrame 2.合流 3.图片抠图透明 (1.)mp4扣yuv图&#xff0c;(2)用1.把一张yuv标记为透明然后av_hwframe_transfer_data到GPU (3)用抠图算法函数对yuv进行处理 (4) qsv的h264_qsv只支持nv12和qsv&#xff0c;但qsv本身并不限制像素格式&#xff0c;比如在…

2023版 STM32实战3 按键外部中断(电路与代码都讲解)

常规电路(带上拉电阻) 阻值可选3.3/4.7/5.1/10 单位K 偷懒电路 利用GPIO内部的上拉模式 代码&#xff08;直接拷贝使用&#xff09; 这是一个外部中断控制变量a增加减少的demo 为了新手方便我直接都写在了main.c文件 #include "stm32f10x.h"u8 keyflag0; u8 a0…

鼠标拖拽拖动盒子时,与盒子内某些点击事件冲突问题解决

目录 问题解决思路解决代码&#xff08;标注【主要代码】的为重点&#xff09; 问题 拖动该悬浮球时&#xff0c;鼠标弹起可能会触发悬浮球内事件 解决思路 鼠标拖动盒子时&#xff0c;将 isMove 设为 true 意为正在拖动盒子&#xff0c;此时将 class"btns_move" 遮…

采用cv2和默认的人脸识别分类器实现人脸检测功能

人脸识别分类器 haarcascade_frontalface_default 提示&#xff1a;分类器文件地址在这里&#xff1a;https://github.com/opencv/opencv/blob/687fc11626901cff09d2b3b5f331fd59190ad4c7/data/haarcascades/haarcascade_frontalface_default.xml 文章目录 人脸识别分类器 haar…

终于把量化入门了,实盘权限已开,学习这件事也不难

多数人18岁就死了&#xff0c;但直到75岁才埋。 ——网易云热评《杀死那个石家庄人》 猫猫挺喜欢这句话的&#xff0c;为什么只活动75岁&#xff0c;于是我查了查现如今78.6岁&#xff0c;大差不差的。 那扣扣减减&#xff0c;人生短短57年&#xff0c;唯一十八岁那年&#xff…

C# 查找迷宫路径

1.导入图像&#xff0c;并且将图像转灰度 using var img new Image<Bgr, byte>(_path); using var grayImg img.Convert<Gray, byte>(); 2.自动二值化图像 using var inputGrayOut new Image<Gray, byte>(grayImg.Size); // 计算OTSU阈值 var threshol…

[正确重装docker] Win10 重装 Docker 提示 Exising installation is up to date 的正确姿势

Win10 重装 Docker 报错 Exising installation is up to date 的一种情况是原来的 docker 没有卸载干净&#xff0c;或者说&#xff0c;没有正确卸载。 巧了&#xff0c;我就是直接删除了&#xff0c;因为一些原因重装了好几次&#xff0c;血泪史留给各位嘲笑。 一条正确的卸…

怎么将几张图片做成pdf合在一起

怎么将几张图片做成pdf合在一起&#xff1f;在我们平时的工作中&#xff0c;图片和pdf都是非常重要的电脑文件&#xff0c;使用也非常频繁&#xff0c;图片能够更为直观的展示内容&#xff0c;而pdf则更加的正规&#xff0c;很多重要文件大多会做成pdf格式的。在职场人的日常工…

力扣刷题-数组-螺旋矩阵

模拟过程&#xff0c;但却十分考察对代码的掌控能力。 重点&#xff1a;循环不变量原则&#xff01; 第一条原则&#xff1a; 模拟顺时针画矩阵的过程&#xff1a; 填充上行从左到右填充右列从上到下填充下行从右到左填充左列从下到上 由外向内一圈一圈这么画下去。 第二条原…

如何在没有第三方.NET库源码的情况,调试第三库代码?

大家好&#xff0c;我是沙漠尽头的狼。 本方首发于Dotnet9&#xff0c;介绍使用dnSpy调试第三方.NET库源码&#xff0c;行文目录&#xff1a; 安装dnSpy编写示例程序调试示例程序调试.NET库原生方法总结 1. 安装dnSpy dnSpy是一款功能强大的.NET程序反编译工具&#xff0c;…

ESP8266 WiFi物联网智能插座—电能计量

目录 1、芯片功能 2、性能指标 3、寄存器说明 4、UART通信协议 4.1、写操作帧格式和时序 4.2、读操作帧格式和时序 4.3、读取全电参数数据包 4.4、配置波特率 4.5、UART保护机制 5、功能说明 5.1、电流电压瞬态波形计量 5.2、有功功率 5.3、有功功率防潜动 5.4、电能计量 5.5、…

摩尔信使MThings实用功能盘点

“冗长的用户手册”与“精简的交互设计”之间势必产生一条信息鸿沟&#xff0c;现在就来盘点一下摩尔信使MThings有哪些隐蔽而实用的功能。 01 数据配置类 一键刷新 功能&#xff1a;快速读取所有位数据、寄存器数据的当前数值。 操作&#xff1a;双击“数值”列表头。 一键…