spring boot——自定义依赖实现自动配置
需求
要实现的功能是:实现一个可以支持minio+oss两种方式,上传下载文件的自定义依赖。其中还包括一些创建桶、删除桶、删除文件等功能,但是最主要的是实现自动配置。
如果对spring理解很深的话,自动配置这些东西很容易理解,但是对于技术小白来讲只能按葫芦画瓢。
首先,StorageManager是我的自定义依赖对外提供的要注入spring的对象,其中的方法是对外提供的方法,方法返回的是StorageService接口。
package cn.chinatelecom.dxsc.park.oss;import cn.chinatelecom.dxsc.park.oss.entity.FileDomain;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.UUID;public class StorageManager {private static final Logger log = LoggerFactory.getLogger(StorageManager.class);private final StorageService storageService;public StorageManager(StorageService storageService) {this.storageService = storageService;}/*** 创建存储桶** @param bucketName 桶名称*/public void createBucket(String bucketName) {storageService.createBucket(bucketName);}/*** 删除桶* @param bucketName 桶名称* @return*/public Boolean removeBucket(String bucketName) {Boolean removeBucket = storageService.removeBucket(bucketName);return removeBucket;}/*** 删除文件* @param fileName* @return*/public Boolean remove(String fileName){return storageService.remove(fileName);}/*** 下载文件** @param filePath 文件路径* @return 文件流*/public InputStream getObject(String filePath) {return storageService.getObject(filePath);}/*** 上传文件* @param file* @return*/public FileDomain saveFile(MultipartFile file) {FileDomain result = new FileDomain();try {InputStream in = file.getInputStream();long size = file.getSize();String contentType = file.getContentType();String fileName = generateName(file.getOriginalFilename());String filePath = generateKey(fileName);String url = storageService.saveFile(in, size, contentType, filePath);result.setSize(size);result.setUrl(url);result.setKey(filePath);result.setType(contentType);} catch (IOException e) {log.error("保存文件失败", e);throw new RuntimeException(e);}return result;}private String generateName(String originalFilename) {return UUID.randomUUID().toString().replace("-", "") + originalFilename.substring(originalFilename.lastIndexOf("."));}private String generateKey(String fileName) {// 2022-10/24/b4c9106e3f574a61841ce4624494f0cc.jpg 在删除和下载文件时需要传入路径才可以删除和下载return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM/dd")) + "/" + fileName;}}
StorageService接口定义:
package cn.chinatelecom.dxsc.park.oss;import io.minio.errors.*;import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;public interface StorageService {/*** 创建存储桶* @param bucketName 桶名称*/void createBucket(String bucketName);/*** 存储文件* @param inputStream 文件流* @param fileSize 文件大小* @param contentType 文件类型* @param keyName 文件名及路径*/String saveFile(InputStream inputStream, long fileSize, String contentType, String keyName);/*** 下载文件* @param filePath 文件路径* @return*/InputStream getObject(String filePath);/*** 删除文件* @param fileName* @return*/Boolean remove(String fileName);/*** 删除桶* @param bucketName 桶名称* @return*/Boolean removeBucket(String bucketName);
}
FileDomain是上传文件时返回的对象,其中包括文件url,文件在服务器的存储路径,文件类型、大小等等可以自己定义,这里没有使用@Data注解而是手写get和set方法,没有引入Lombok依赖是因为在实现自定义依赖的时候,尽量能不引入依赖就不引入依赖。如果你在自定义依赖里面引入版本2.0的Lombok,但是当别人引用你的依赖的时候,同时也引用的1.0的Lombok,这样就会出现最让人头疼的版本冲突问题,为了避免这种问题出现,尽量不要在底层引用过多依赖。
package cn.chinatelecom.dxsc.park.oss.entity;public class FileDomain {/*** 路径名称*/private String key;/*** 文件url*/private String url;/*** 文件类型(contentType)*/private String type;/*** 文件大小*/private Long size;public String getKey() {return key;}public void setKey(String key) {this.key = key;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getType() {return type;}public void setType(String type) {this.type = type;}public Long getSize() {return size;}public void setSize(Long size) {this.size = size;}
}
接下来是自动配置的相关代码
DxscStorageProperties是需要配置的一些属性,比如accessKey以及accessSecret等等。
@ConfigurationProperties注解,在 SpringBoot 中,当想需要获取到配置文件数据时,除了可以用 Spring 自带的 @Value 注解外,SpringBoot 还提供了一种更加方便的方式:@ConfigurationProperties。只要在 Bean 上添加上了这个注解,指定好配置文件的前缀,那么对应的配置文件数据就会自动填充到 Bean 中。
package cn.chinatelecom.dxsc.park.oss.config;import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties(prefix = "dxsc.storage")
public class DxscStorageProperties {/*** 激活哪种存储*/private String active;/*** oss地址*/private String endpoint;/*** 桶名称*/private String bucketName;private String accessKey;private String accessSecret;public String getActive() {return active;}public void setActive(String active) {this.active = active;}public String getEndpoint() {return endpoint;}public void setEndpoint(String endpoint) {this.endpoint = endpoint;}public String getBucketName() {return bucketName;}public void setBucketName(String bucketName) {this.bucketName = bucketName;}public String getAccessKey() {return accessKey;}public void setAccessKey(String accessKey) {this.accessKey = accessKey;}public String getAccessSecret() {return accessSecret;}public void setAccessSecret(String accessSecret) {this.accessSecret = accessSecret;}}
接下来的代码才是重中之重DxscStorageAutoConfiguration,此类将DxscStorageProperties注入到spring中,Spring Boot 会扫描到类路径下的META-INF/spring.factories配置文件,把DxscStorageAutoConfiguration对应的的Bean值添加到容器中。
package cn.chinatelecom.dxsc.park.oss.autoconfigure;import cn.chinatelecom.dxsc.park.oss.StorageManager;
import cn.chinatelecom.dxsc.park.oss.StorageService;
import cn.chinatelecom.dxsc.park.oss.config.DxscStorageProperties;
import cn.chinatelecom.dxsc.park.oss.impl.MinioStorageServiceImpl;
import cn.chinatelecom.dxsc.park.oss.impl.OssStorageServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@EnableConfigurationProperties(DxscStorageProperties.class)
@Configuration
public class DxscStorageAutoConfiguration {@Autowiredprivate DxscStorageProperties dxscStorageProperties;@Beanpublic StorageManager storageManager(StorageService storageService) {return new StorageManager(storageService);}@Bean@ConditionalOnProperty(prefix = "dxsc.storage", value = "active", havingValue = "minio")public StorageService minioService() {return new MinioStorageServiceImpl(dxscStorageProperties);}
//prefix为前缀,value为选择哪个属性,havingValue就是启用哪种方式,当active属性为minio时就调用上面的,oss就调用下面的,
//大概就是这个意思@Bean@ConditionalOnProperty(prefix = "dxsc.storage", value = "active", havingValue = "oss")public StorageService OssService() {return new OssStorageServiceImpl(dxscStorageProperties);}}
spring.factories文件:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.chinatelecom.dxsc.park.oss.autoconfigure.DxscStorageAutoConfiguration

其中MinioStorageServiceImpl和OssStorageServiceImpl就是实现的文件上传下载的接口,再此展示minion的接口:
oss接口可以查看官方文档,其中写得很清楚。
package cn.chinatelecom.dxsc.park.oss.impl;import cn.chinatelecom.dxsc.park.oss.StorageService;
import cn.chinatelecom.dxsc.park.oss.config.DxscStorageProperties;
import io.minio.*;
import io.minio.http.Method;import java.io.InputStream;public class MinioStorageServiceImpl implements StorageService {private final DxscStorageProperties dxscStorageProperties;private final MinioClient minioClient;public MinioStorageServiceImpl(DxscStorageProperties dxscStorageProperties) {this.dxscStorageProperties =dxscStorageProperties;this.minioClient = MinioClient.builder().endpoint(dxscStorageProperties.getEndpoint()).credentials(dxscStorageProperties.getAccessKey(), dxscStorageProperties.getAccessSecret()).build();}@Overridepublic void createBucket(String bucketName){boolean isExist = false;// 检查存储桶是否已经存在try{isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());if (!isExist) {//创建桶minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config("{\"Version\":\"2023-2-20\",\"Statement\":[]}").build());}}catch (Exception e){throw new RuntimeException(e);}}@Overridepublic String saveFile(InputStream inputStream, long fileSize, String contentType, String keyName) {try {PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(dxscStorageProperties.getBucketName()).object(keyName).stream(inputStream, fileSize, -1).contentType(contentType).build();//文件名称相同会覆盖minioClient.putObject(objectArgs);// 查看文件地址GetPresignedObjectUrlArgs build = new GetPresignedObjectUrlArgs().builder().bucket(dxscStorageProperties.getBucketName()).object(keyName).method(Method.GET).build();try {return minioClient.getPresignedObjectUrl(build);} catch (Exception e) {throw new RuntimeException(e);}} catch (Exception e) {throw new RuntimeException(e);}}@Overridepublic InputStream getObject(String filePath) {try{GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(dxscStorageProperties.getBucketName()).object(filePath).build();return minioClient.getObject(objectArgs);}catch (Exception e){System.out.println("下载失败");throw new RuntimeException(e);}}@Overridepublic Boolean remove(String fileName) {try {minioClient.removeObject(RemoveObjectArgs.builder().bucket(dxscStorageProperties.getBucketName()).object(fileName).build());} catch (Exception e) {return false;}return true;}@Overridepublic Boolean removeBucket(String bucketName) {try {//在删除桶时,需要注意一下不能删除自身,当你的配置文件里的bucketName的test时还要删除test,是不能删除成功的//bucketName为test1时,删除test,是可以的minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());} catch (Exception e) {return false;}return true;}
}
至此,自定义依赖完成,接下来是引用,引用方法为先将自定义依赖上传至本地maven库,然后才可以导入依赖,
<dependency><artifactId>dxsc-park-frame-oss</artifactId><groupId>cn.chinatelecom</groupId><version>1.0.0-SNAPSHOT</version></dependency>
然后,配置minio或者oss的accessKey和accessSecret等属性:
(active为minio就是通过minion方式实现文件上传下载)
dxsc:storage:active: minioendpoint: http://***.168.30.27:9000bucketName: dxsc-parkaccessKey: minioadminaccessSecret: minioadmin
# storage:
# active: oss
# endpoint: https://oss-cn-beijing.aliyuncs.com
# bucketName: dxsc-park
# accessKey: ***
# accessSecret: ***
server:port: 1101
最后注入StorageManager,在一开始的时候就说过StorageManager是对外提供的:
package cn.chinatelecom.dxsc.park.test.controller;import cn.chinatelecom.dxsc.park.oss.StorageManager;
import cn.chinatelecom.dxsc.park.oss.entity.FileDomain;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;@RestController
@RequestMapping("/minio")
public class TestController {@Autowiredprivate StorageManager storageManager;@PostMapping("/createBucket")public void createBucket(@RequestParam("bucketName") String bucketName){storageManager.createBucket(bucketName);}@PostMapping("/removeBucket")public void removeBucket(@RequestParam("bucketName") String bucketName){storageManager.removeBucket(bucketName);}@PostMapping("/upload")public String upload(@RequestParam("file") MultipartFile file){FileDomain fileDomain = storageManager.saveFile(file);System.out.println(fileDomain.getUrl());return fileDomain.getKey();}@PostMapping("/remove")public Boolean remove(@RequestParam("fileName") String fileName){Boolean remove = storageManager.remove(fileName);return remove;}@PostMapping("/getObject")public void getObject(@RequestParam("filePath") String filePath, HttpServletResponse response) throws IOException {InputStream object = storageManager.getObject(filePath);response.setContentType("application/octet-stream");response.addHeader("Content-Disposition", "attachment;filename=" + "fileName");IOUtils.copy(object, response.getOutputStream());IOUtils.closeQuietly(object);}}
以上就是我在实现自动配置的全部内容
一个集坚强与自信于一身的菇凉。
相关文章:
spring boot——自定义依赖实现自动配置
需求 要实现的功能是:实现一个可以支持miniooss两种方式,上传下载文件的自定义依赖。其中还包括一些创建桶、删除桶、删除文件等功能,但是最主要的是实现自动配置。 如果对spring理解很深的话,自动配置这些东西很容易理解&#…...
QMap 判断是否value是否已经存在,结合Sleep函数测试
网上查了资料,基本说的都是通过.value判断是否已经之前的key值,但是尝试.了一下发现有.key的函数,对比着来就感觉这个函数是用来判断是否已经存在value值,于是开始百度也几乎没有找到相关资料,只好自己看官方文档&…...
vue后台管理系统项目-table选择多行数据分页列表、一键全选重置功能
table选择多行数据 功能介绍: 1.列表分页功能; 2.一键全选,选中列表所有数据; 3.全选,选中当前页数据; 4.重置,清除选中状态; 5.列表搜索查询; 效果: 1.列表分…...
论文解读 | [CVPR2019] 基于自适应文本区域表示的任意形状场景文本检测
目录 1 研究背景及意义 2 总体设计 3 方法论 3.1 自适应文本区域表示 3.2 文本建议 3.3 建议改进 4 损失函数 5 实验及结果 1 研究背景及意义 现有的场景文本检测方法使用固定点数的多边形来 表示文本区域。例如,水平文本使用2个点(左上/右下)表示文本区域&…...
2月编程语言排行榜谁还没有看?
近日,TIOBE公布了2023年2月编程语言排行榜,本月各个语言表现如何?谁又摘得桂冠?一起来看看吧! TIOBE 2月Top15编程语言: 详细榜单查看TIOBE官网 https://www.tiobe.com/tiobe-index/ 关注IT行业的小伙伴们…...
nginx.conf配置方法详细介绍
从前面的内容学习中,我们知道Nginx的核心配置文件默认是放在/usr/local/nginx/conf/nginx.conf,这一节,我们就来学习下nginx.conf的内容和基本配置方法。读取Nginx自带的Nginx配置文件,我们将其中的注释部分【学习一个技术点就是在…...
【微信小程序】一文带你吃透开发中的常用组件
写在前面 小程序中的组件也是由宿主环境提供的,开发者可以基于组件快速搭建出漂亮的页面结构。 官方把小程序的组件分为了9大类,分别是: 1.视图容器 2.基础内容 3.表单组件 4.导航组件 5.媒体组件 6.地图组件 7.画布组件 …...
Nginx 部署 Vue 项目以及 Vue 项目刷新出现 404 的问题(完整步骤)(亲测有效)
Nginx 部署 Vue 项目以及 Vue 项目刷新出现 404 的问题(完整步骤)(亲测有效) 1.流程步骤(本教程下载的是1.20.2版本,放在D盘) 1-1. 首先去官方下载 nginx ,然后在当前目录下创建ht…...
leaflet 加载geojson数据,随机显示不同颜色的circleMarker
第086个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet项目中加载geojson数据,随机显示不同颜色的circleMarker. 直接复制下面的 vue+leaflet源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共89行)相关API专栏目标示例效果 配置方式…...
UL grant的分配(LCP)
欢迎关注同名微信公众号“modem协议笔记”。 UE有UL data时,会发送BSR的告知网络侧自己详细的请求,期望网络能够如期下发UL grant,正常情况下网络侧会给UE足够的UL grant去发送UL data,整个过程都会比较顺利。UE收到UL grant后&a…...
真我air笔记本电脑怎么重装Win10系统?
真我air笔记本电脑怎么重装Win10系统?最近真我air笔记本电脑挺多用户购买的,因为这款电脑性价比比较高,适合学生和一些办公人员来使用。但是系统预制了Win11系统,有用户想要将系统重装到Win10来使用。那么如何去进行系统的重装呢&…...
【闲聊杂谈】深入剖析SpringCloud Alibaba之Nacos源码
Nacos核心功能点 服务注册 Nacos Client会通过发送REST请求的方式向Nacos Server注册自己的服务,提供自身的元数据,比如ip地址、端口等信息。Nacos Server接收到注册请求后,就会把这些元数据信息存储在一个双层的内存Map中; 服…...
MySQL删除或清空表内数据的方法
MySQL删除或清空表内数据的方法 一、使用MySQL清空表数据命令:truncate SQL语法为: truncate table 表名注意: truncate该命令会直接将数据表内数据清空;truncate该命令删除数据后会重置Identity(标识列、自增字段…...
Android 权限(二): 动态权限讲解
1. 前言 继上一篇文章说到Android权限汇总, 请移步笔者的文章Android 权限(一):权限大全_broadview_java的博客-CSDN博客_android 仅使用中允许权限 先要理清楚权限分类和定义,本篇文章继续说一下动态权限的申请和框架层的实现流程, 以及如何实现赋予系统应用默认的…...
【C++】2.类和对象(上)
1.面向过程和面向对象 C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。C是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。 2.类的引入…...
扬帆优配|3300点半日游!上证指数冲高回落;再迎重磅利好!
今天早盘,A股冲高回落,上证指数3300点得而复失,深证成指也于12000点无功而返。 盘面上,煤炭、钢铁、房地产、才智政务等板块涨幅居前,酿酒、酒店餐饮、日用化工、IT设备等板块跌幅居前。北上资金净流入7.77亿元。 房地…...
如何编写性能测试计划?一篇文章教你设计符合项目的性能测试计划
上篇文章,我们讲过性能测试计划,接下来我们就来讲讲如何设计符合项目的性能测试计划。到上篇为止,我们了解了性能测试计划中包含的内容,但是,这个颗粒度,我觉得作为一名测试经验不够丰富的性能工程师来说&a…...
第3章 Windows 下安装 Memcached教程
官网上并未提供 Memcached 的 Windows 平台install 包,咱们可以使用以下链接来download ,需要根据自己的去下载: 点击下载 在 1.4.5 版本以前 memcached 可以作为一个服务install ,而在 1.4.5 及之后的版本删除了该功能。因此咱…...
RXjava中的操作符
要使用Rxjava首先要导入两个包,其中rxandroid是rxjava在android中的扩展 implementation io.reactivex:rxandroid:1.2.1implementation io.reactivex:rxjava:1.2.0Rxjava中的操作符 创建型操作符 interval 创建一个按固定时间间隔发射整数序列的Observable…...
前端页面jquery规范写法
使用最新版本的 jQuery 最新版本的 jQuery 会改进性能和增加新功能,若不是为了兼容旧浏览器,建议使用最新版本的 jQuery。以下是三条常见的 jQuery 语句,版本越新,性能越好: $(.elem) $(.elem, context) context.find(.elem) 结果 1.6.2 版执行次数远超两个老版本。 jQ…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
