提供spring boot扩展包,包含自动装配、starter、一些工具类等。
1. 关于
1.1. 简介
spring-boot-extension是一个拓展Spring Boot的库,内置一些SpringBoot未包含的Starter包
同时也补充的各种工具类,用于简化开发、提升开发效率
项目按职责拆分为几个部分:
-
spring-auto-service:编译期注解处理器,用于生成 Spring Boot 自动装配文件、spring.factories和aot.factories。 -
spring-extension-commons:通用工具、表达式解析、Jackson、HTTP 客户端、Servlet 包装等基础能力。 -
spring-extension-context:第三方组件与 Spring 的集成能力,例如 Curator、Disruptor、动态数据源、限流、分布式锁、二维码、MyBatis 等。 -
spring-boot-extension-autoconfigure:面向 Spring Boot 的自动配置模块。 -
spring-boot-extension-starters:按功能拆分的 starter,便于业务项目按需引入。 -
spring-testcontainers-support:补充 Testcontainers 与 Spring Boot service connection 的适配。
推荐业务项目优先引入 BOM,然后只引入需要的 starter。需要自定义扩展时,再直接使用 spring-extension-commons
或 spring-extension-context 中的基础 API。
2. 安装
2.1. 依赖相关
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.github.livk-cloud</groupId>
<artifactId>spring-extension-dependencies</artifactId>
<version>${version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
implementation platform('io.github.livk-cloud:spring-extension-dependencies:$version')
implementation(platform("io.github.livk-cloud:spring-extension-dependencies:${version}"))
最小BOM依赖
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.github.livk-cloud</groupId>
<artifactId>spring-extension-bom</artifactId>
<version>${version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
implementation platform('io.github.livk-cloud:spring-extension-bom:$version')
implementation(platform("io.github.livk-cloud:spring-extension-bom:${version}"))
2.3. 使用建议
spring-extension-dependencies 管理项目自身依赖及常用第三方依赖版本,适合直接接入业务项目。
spring-extension-bom 更轻量,只管理 spring-boot-extension 发布的模块版本,适合已经有统一依赖治理的平台项目。
引入 BOM 后,业务模块中的 starter 可以省略版本:
dependencies {
implementation(platform("io.github.livk-cloud:spring-extension-dependencies:${version}"))
implementation("io.github.livk-cloud:redisson-spring-boot-starter")
implementation("io.github.livk-cloud:distributed-lock-spring-boot-starter")
}
如果只是使用工具类,不需要 Spring Boot 自动配置,可以只引入 spring-extension-commons。
3. 使用指导
3.1. spring boot装配文件自动生成
根据代码定义生成spring boot的自动装配文件和spring.factories、aot.factories
该模块是编译期注解处理器,注解保留策略为 SOURCE。
它不会在运行时参与逻辑,只负责在编译阶段根据注解生成 Spring 约定位置的资源文件。
<dependency>
<groupId>io.github.livk-cloud</groupId>
<artifactId>spring-auto-service</artifactId>
<version>${version}</version>
<scope>provided</scope>
</dependency>
compileOnly 'io.github.livk-cloud:spring-auto-service:${version}'
annotationProcessor 'io.github.livk-cloud:spring-auto-service:${version}'
compileOnly("io.github.livk-cloud:spring-auto-service:${version}")
annotationProcessor("io.github.livk-cloud:spring-auto-service:${version}")
3.1.1. @SpringAutoService使用示例
@SpringAutoService 默认生成 Spring Boot 自动配置导入文件:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
如果传入自定义注解类型,会生成该注解对应的 imports 文件,适合配合 @AutoImport 或 SpringAbstractImportSelector
使用。
@Component
@SpringAutoService
public class SpringContextHolder implements BeanFactoryAware, ApplicationContextAware, DisposableBean {
}
生成文件 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.livk.commons.spring.context.SpringContextHolder
@AutoConfiguration
@ConditionalOnClass(WebClient.class)
@SpringAutoService(com.livk.commons.http.annotation.EnableWebClient.class)
public class WebClientConfiguration {
}
生成文件 META-INF/spring/com.livk.commons.http.annotation.EnableWebClient.imports
com.livk.commons.http.WebClientConfiguration
3.1.2. @SpringFactories 使用示例
指定接口为spring.factories的Key
@SpringFactories(org.springframework.boot.env.EnvironmentPostProcessor)
public class TraceEnvironmentPostProcessor implements EnvironmentPostProcessor {
}
生成文件 META-INF/spring.factories
org.springframework.boot.env.EnvironmentPostProcessor=\
com.livk.commons.spring.TraceEnvironmentPostProcessor
当前类如果仅仅只有一个接口,可以不指定,自动生成
@SpringFactories
public class TraceEnvironmentPostProcessor implements EnvironmentPostProcessor {
}
生成文件 META-INF/spring.factories
org.springframework.boot.env.EnvironmentPostProcessor=\
com.livk.commons.spring.TraceEnvironmentPostProcessor
3.1.3. @AotFactories 使用示例
指定接口为aot.factories的Key
@AotFactories(org.springframework.boot.env.EnvironmentPostProcessor)
public class TraceEnvironmentPostProcessor implements EnvironmentPostProcessor {
}
生成文件 META-INF/spring/aot.factories
org.springframework.boot.env.EnvironmentPostProcessor=\
com.livk.commons.spring.TraceEnvironmentPostProcessor
3.1.4. 使用注意
-
当前类只实现一个接口时,
@SpringFactories和@AotFactories可以自动推断 key。 -
当前类实现多个接口时,建议显式指定
value,避免生成结果不符合预期。 -
@SpringAutoService标记的类通常也会带有 Spring Boot 的@AutoConfiguration或普通配置类注解。 -
Gradle 项目需要同时配置
compileOnly与annotationProcessor,否则编译期无法执行处理器。
当前类如果仅仅只有一个接口,可以不指定,自动生成
@AotFactories
public class TraceEnvironmentPostProcessor implements EnvironmentPostProcessor {
}
生成文件 META-INF/spring/aot.factories
org.springframework.boot.env.EnvironmentPostProcessor=\
com.livk.commons.spring.TraceEnvironmentPostProcessor
3.2. starter 选择指南
spring-boot-extension-starters 按功能拆分 starter,业务项目建议按需引入。
starter 会带上对应的 context 与 autoconfigure 模块,通常不需要再手动引入底层模块。
3.2.1. starter 与功能对应关系
| starter | 核心能力 | 常见依赖/前置条件 |
|---|---|---|
curator-spring-boot-starter |
Zookeeper Curator 客户端、 |
需要可访问的 Zookeeper |
disruptor-spring-boot-starter |
Disruptor 事件队列扫描、生产者与消费者集成 |
需要声明
|
dynamic-datasource-spring-boot-starter |
动态数据源、数据源切换 AOP |
需要配置 |
fesod-spring-boot-starter |
Excel 导入导出,支持 MVC 与 WebFlux |
需要定义 Excel 解析 listener |
limit-spring-boot-starter |
方法级限流 |
分布式场景建议配合 Redisson |
distributed-lock-spring-boot-starter |
方法级分布式锁 |
需要 Redisson 或 Curator 作为锁实现 |
mybatis-extension-boot-starter |
MyBatis 字段注入、SQL 耗时监控、JSON 类型处理支持 |
需要 MyBatis 环境 |
qrcode-spring-boot-starter |
二维码请求解析与响应渲染 |
支持 MVC 与 WebFlux |
redisearch-spring-boot-starter |
RediSearch 连接工厂与模板 |
需要 Redis Stack / RediSearch 服务 |
redisson-spring-boot-starter |
RedissonClient 自动配置 |
需要 Redis 或 Redis Cluster |
browscap-spring-boot-starter |
基于 BrowsCap 的 User-Agent 解析 |
适合使用 BrowsCap 数据的项目 |
yauaa-spring-boot-starter |
基于 YAUAA 的 User-Agent 解析 |
适合需要 YAUAA 解析能力的项目 |
3.2.2. 依赖示例
<dependency>
<groupId>io.github.livk-cloud</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>io.github.livk-cloud</groupId>
<artifactId>distributed-lock-spring-boot-starter</artifactId>
</dependency>
implementation 'io.github.livk-cloud:redisson-spring-boot-starter'
implementation 'io.github.livk-cloud:distributed-lock-spring-boot-starter'
implementation("io.github.livk-cloud:redisson-spring-boot-starter")
implementation("io.github.livk-cloud:distributed-lock-spring-boot-starter")
3.2.3. 选择建议
如果只使用通用工具类,选择 spring-extension-commons。
如果需要手动组装基础能力,选择 spring-extension-context。
如果是 Spring Boot 业务项目,优先选择具体 starter。
多个 starter 可以组合使用,例如:
-
redisson-spring-boot-starter+distributed-lock-spring-boot-starter:使用 Redisson 分布式锁。 -
redisson-spring-boot-starter+limit-spring-boot-starter:使用 Redisson 做分布式限流。 -
curator-spring-boot-starter+distributed-lock-spring-boot-starter:使用 Curator 分布式锁。 -
browscap-spring-boot-starter或yauaa-spring-boot-starter:二选一接入 User-Agent 解析。
不建议一次性引入所有 starter。按功能引入可以减少无关第三方依赖,也能避免多个同类 Bean 同时存在导致自动配置选择不符合预期。
3.3. spring通用工具拓展
提供一些通用、工具类方便开发
该模块不强制绑定具体业务场景,适合在 starter、自定义自动配置和普通业务代码中复用。
其中 annotation、aop、expression
更偏扩展开发;http、jackson、web、util、io
更偏日常业务工具。
<dependency>
<groupId>io.github.livk-cloud</groupId>
<artifactId>spring-extension-commons</artifactId>
<version>${version}</version>
</dependency>
implementation 'io.github.livk-cloud:spring-extension-commons:${version}'
implementation("io.github.livk-cloud:spring-extension-commons:${version}")
3.3.1. annotation
annotation 包用于简化基于注解的 Spring 扩展开发,适合自定义 @EnableXxx、扫描器或自动导入配置类的场景。
SpringAbstractImportSelector
通过继承AutoConfigurationImportSelector,可以实现自动导入spring的配置文件,导入文件为META-INF/spring/{annotationClass}.imports,需要使用org.springframework.context.annotation.Import进行注册
使用示例:
public @interface Http{
}
public class HttpImportSelector extends SpringAbstractImportSelector<Http>{
}
@AutoImport
使用AutoImport自动进行注册,作用于注解上,功能同SpringAbstractImportSelector类似
使用示例:
@AutoImport
public @interface Http{
}
编译后的配置文件示例:
META-INF/spring/com.example.Http.imports
文件内容为需要导入的配置类全限定名,每行一个。
3.3.2. aop
aop 包封装了基于注解的 Advisor 模板,减少重复编写 Pointcut、MethodInterceptor
和注解解析逻辑。
AbstractAnnotationAdvisor
基于注解的切面拦截器基类,自动解析方法或类上的注解并传递给 doInvoke。
使用示例
public class LockInterceptor extends AbstractAnnotationAdvisor<OnLock> {
@Override
protected Object doInvoke(MethodInvocation invocation, OnLock onLock) throws Throwable {
// AOP 处理,等同于 @Around
return invocation.proceed();
}
@Override
public Pointcut getPointcut() {
// 实现切入点
return AnnotationPointcutFactory.forMethod().create(annotationType);
}
}
将注解作为泛型参数 <A>,基类会自动在方法和类两个层级查找注解并传入 doInvoke。
AbstractAnnotationPointcutStrategyAdvisor
AbstractAnnotationAdvisor 的扩展,通过 AnnotationPointcutFactory 策略自动生成
Pointcut,无需手动实现 getPointcut()。
使用示例
public class LockInterceptor extends AbstractAnnotationPointcutStrategyAdvisor<OnLock> {
@Override
protected Object doInvoke(MethodInvocation invocation, OnLock onLock) throws Throwable {
// 拦截处理
return invocation.proceed();
}
@Override
protected AnnotationPointcutFactory pointcutStrategy() {
// 默认实现为 AnnotationPointcutFactory.forTarget()
return AnnotationPointcutFactory.forTarget();
}
}
AnnotationPointcutFactory
切入点工厂接口,提供多种基于注解的切入点创建策略:
-
forType()— 基于类级别注解拦截,等价于@Around(@within(Annotation)) -
forMethod()— 基于方法级别注解拦截,等价于@Around(@annotation(Annotation)) -
forTypeOrMethod()— 基于类或方法级别注解拦截,等价于@Around(@annotation(Annotation) || @within(Annotation)) -
forTarget()— 根据注解的@Target声明自动推断:-
如果仅有
TYPE,则为类级别 -
如果仅有
METHOD,则为方法级别 -
如果同时声明了
TYPE和METHOD,则为 TYPE_OR_METHOD 级别 -
以上条件都不满足则抛出异常
-
3.3.3. expression
expression 包提供统一的表达式解析接口,将方法参数、Map 等上下文包装为
Context,再交给不同表达式引擎计算。
限流、分布式锁等组件可以用它解析动态 key。
ExpressionResolver
表达式解析的顶层接口,采用 Fluent API 设计:
ExpressionResolver resolver = new SpringExpressionResolver();
// 方式一:通过 Method 参数构建上下文
String result = resolver.resolve("#username")
.method(method, args)
.evaluate();
// 方式二:直接传入 Map 作为上下文
String result = resolver.resolve("#name")
.context(Map.of("name", "livk"))
.evaluate();
// 方式三:指定返回类型
Integer count = resolver.resolve("#count")
.context(Map.of("count", 10))
.evaluate(Integer.class);
CacheExpressionResolver
ExpressionResolver 的抽象实现,内置表达式编译缓存和 Spring Environment
占位符解析支持。
子类只需实现三个模板方法:
public class MyExpressionResolver extends CacheExpressionResolver<MyContext, MyExpression> {
// 将表达式字符串编译成引擎特定的表达式对象
@Override
protected MyExpression compile(String expression) {
return MyEngine.parse(expression);
}
// 将通用 Context 转换为引擎特定的上下文
@Override
protected MyContext transform(Context context) {
return new MyContext(context.asMap());
}
// 使用编译后的表达式和引擎上下文进行计算
@Override
protected <T> T calculate(MyExpression expression, MyContext context, Class<T> returnType) {
return expression.evaluate(context, returnType);
}
}
可选重写 wrapIfNecessary(String) 对原始表达式做预处理(例如 SpEL 的 #{…}
模板语法)。
Context
表达式求值上下文,持有变量绑定。支持链式构建:
Context context = Context.create()
.put("username", "livk")
.put("count", 10);
Map<String, Object> map = context.asMap();
也可以从已有 Map 创建:
Context context = Context.create(Map.of("key", "value"));
ContextFactory
用于把方法运行时信息转换为 Context。默认实现 DefaultContextFactory 使用
Spring 的 ParameterNameDiscoverer 按参数名暴露变量。
ContextFactory factory = ContextFactory.DEFAULT_FACTORY;
Context context = factory.create(method, args);
// context 中包含方法参数名到实际值的映射
在 ExpressionResolver 的 Fluent API 中,可通过
contextFactory(…) 替换默认实现:
resolver.resolve("#param")
.contextFactory(myCustomFactory)
.method(method, args)
.evaluate();
内置 ExpressionResolver
| 实现类 | 表达式引擎 | 额外依赖 |
|---|---|---|
|
Spring EL |
无(spring-expression 已传递) |
|
Aviator |
需引入 aviator jar |
|
FreeMarker |
需引入 freemarker jar |
|
Apache Commons Jexl3 |
需引入 commons-jexl3 jar |
|
Mvel 2 |
需引入 mvel2 jar |
3.3.4. http
http 包提供 HTTP 客户端自动导入注解,可按需创建
WebClient、RestClient 或 OkHttp 支持组件。
EnableHttpClient
EnableHttpClient会根据value值自动导入对应的http客户端
使用示例:
@Slf4j
@EnableHttpClient({
HttpClientType.WEB_CLIENT,
HttpClientType.REST_CLIENT
})
public class App {
@Bean
public ApplicationRunner applicationRunner(WebClient webClient,
RestClient restClient) {
return args -> {
log.info("webClient:{}", webClient);
log.info("restClient:{}", restClient);
};
}
}
同时提供了@EnableRestClient、@EnableWebClient作为快捷方式
3.3.5. jackson
jackson 包提供 JSON 序列化、反序列化、JsonNode 操作和类型构造的便捷封装。
JacksonSupport
Jackson便捷开发工具,通过包装ObjectMapper实现各种JSON转换
使用示例:
public static void main(String[] args){
JacksonSupport json = new JacksonSupport(new JsonMapper());
}
各种工具类
com.livk.commons.jackson.JsonMapperUtils 用于JsonMapper默认配置进行快速序列化和反序列化
com.livk.commons.jackson.JsonNodeUtils 用于JsonNode快速操作
com.livk.commons.jackson.TypeFactoryUtils 用于获取Jackson类型工具
3.3.6. util
util 包收纳通用工具类,可以按用途大致分为:
-
注解与反射:
AnnotationMetadataResolver、AnnotationFinder、AnnotationUtils、ReflectionUtils、ClassUtils、FieldUtils、TypeUtils。 -
Bean 与泛型:
BeanLambda、BeanLambdaDescriptor、BeanUtils、BeanConverter、GenericWrapper、RecordWrapper。 -
Stream 与集合:
StreamUtils、EnumerationSpliterator。 -
Web 辅助:
HttpServletUtils、HttpReactiveUtils、MultiValueMapSplitter。 -
数据结构与 ID:
Pair、TreeNode、SnowflakeIdGenerator。 -
日期时间:
Jsr310Utils。 -
配置与上下文:
YamlUtils、ContextSnapshots。
AnnotationMetadataResolver
根据包名查找含有某些注解的类。
使用示例:
AnnotationMetadataResolver resolver = new AnnotationMetadataResolver();
resolver.find(MyAnnotation.class, "com.livk.resolver");
BeanLambda
根据 lambda 表达式解析 Field 和 Method 信息。
使用示例:
String methodName = BeanLambda.methodName(Maker::getNo);
Method method = BeanLambda.method(Maker::getNo);
String fieldName = BeanLambda.fieldName(Maker::getNo);
Field field = BeanLambda.field(Maker::getNo);
BeanConverter
Bean 与 Map 之间的转换工具类,基于 Spring BeanWrapper 实现。
使用示例:
// Bean 转 Map
Map<String, Object> map = BeanConverter.toMap(myBean);
ContextSnapshots
提供对线程池等进行包装,实现 Micrometer 行为传播(Observation / Baggage 跨线程传递)。
使用示例:
ExecutorService service = Executors.newCachedThreadPool();
ExecutorService wrap = ContextSnapshots.wrap(service);
wrap.submit(() -> {
// 此处可获取到父线程的 Observation 上下文
});
GenericWrapper
进行类包装的通用接口,可用于统一包裹任意类型。
使用示例:
GenericWrapper<?> wrapper = GenericWrapper.of("123");
Object value = wrapper.unwrap();
StreamUtils
Stream 工具类,提供数组转 Stream、带索引遍历、去重过滤、合并等增强能力。
使用示例:
// Enumeration 转 Stream
Stream<String> stream = StreamUtils.of(enumeration);
// 带索引遍历
StreamUtils.forEachWithIndex(list, (index, item) -> {});
TypeUtils
Type 相关工具类,提供 Type 到 Class 的转换以及泛型参数解析能力。
使用示例:
// 解析类的第一个泛型参数类型
Class<?> argType = TypeUtils.resolveTypeArgument(MyService.class, BaseService.class);
MultiValueMapSplitter
字符串分割成 MultiValueMap。
使用示例:
String str = "root=1,2,3&root=4&a=b&a=c";
MultiValueMap<String, String> multiValueMap = MultiValueMapSplitter.of("&", "=").split(str, ",");
// {root=[1, 2, 3, 4], a=[b, c]}
Pair
一个简单的 KV 键值对,Jackson 序列化时按照 Map.Entry 方式适配。
使用示例:
Pair<String, String> pair = Pair.of("username", "password");
3.3.7. io
io 包提供 IO 相关的工具类,涵盖 DataBuffer 转换、资源扫描、GZip 压缩和文件操作。
DataBufferConverter
DataBuffer 格式转换工具类,提供
Flux<DataBuffer>、InputStream、byte[] 之间的相互转换。适用于
WebFlux 场景。
使用示例:
// Flux<DataBuffer> 转 Mono<InputStream>
Mono<InputStream> inputStreamMono = DataBufferConverter.transform(dataBufferFlux);
// byte[] 转 Flux<DataBuffer>
Flux<DataBuffer> flux = DataBufferConverter.transform(bytes);
// Flux<DataBuffer> 转 Mono<byte[]>
Mono<byte[]> bytesMono = DataBufferConverter.transformByte(dataBufferFlux);
// InputStream 转 Flux<DataBuffer>
Flux<DataBuffer> flux = DataBufferConverter.transform(inputStream);
ResourceScanner
资源扫描工具类,基于 Spring 的 PathMatchingResourcePatternResolver 封装,简化
classpath 资源获取。
使用示例:
// 获取单个资源
Resource resource = ResourceScanner.getResource("classpath:config/app.yml");
// 获取匹配模式的多个资源
Resource[] resources = ResourceScanner.getResources("classpath*:META-INF/spring/*.imports");
GzipUtils
GZip 压缩与解压缩工具类。
使用示例:
// 压缩
ByteArrayOutputStream out = new ByteArrayOutputStream();
GzipUtils.compress(data, out);
// 解压
byte[] decompressed = GzipUtils.decompress(inputStream);
3.3.8. web
web 包主要面向 Servlet 场景,提供请求参数、请求体和响应体包装能力。
适合在 Filter、Interceptor 或测试中改写请求/响应内容。
HttpParameters
http请求Parameter参数,类似于org.springframework.http.HttpHeaders
使用示例:
public class Main{
public static void main(String[] args){
MockHttpServletRequest request = new MockHttpServletRequest();
request.addParameter("username", "livk", "root", "admin");
request.addParameter("password", "123456");
MultiValueMap<String, String> params = WebUtils.params(request);
HttpParameters parameters = new HttpParameters(params);
}
}
RequestWrapper
HttpServletRequest包装类,用于修改body、添加header、添加param
使用示例:
public class Main{
public static void main(String[] args){
MockHttpServletRequest request = new MockHttpServletRequest();
RequestWrapper wrapper = new RequestWrapper(request);
wrapper.body(JsonMapperUtils.writeValueAsBytes(Map.of("root", "root")));
wrapper.addHeader("Content-Type", "application/json");
wrapper.addParameter("username", "livk");
wrapper.addParameter("username", new String[]{"root", "admin"});
}
}
ResponseWrapper
HttpServletResponse包装类,用于修改body
使用示例:
public class Main{
public static void main(String[] args){
MockHttpServletResponse response = new MockHttpServletResponse();
ResponseWrapper wrapper = new ResponseWrapper(response);
wrapper.replaceBody(JsonMapperUtils.writeValueAsBytes(Map.of("root", "root")));
}
}
3.4. spring 组件拓展
兼容Spring的基础包
提供一些第三方包与spring整合的拓展,包括一些自定义拓展
如果只需要基础 API、自定义 Bean 或手动装配,可以引入 spring-extension-context。
如果希望交给 Spring Boot 自动配置,业务项目通常直接引入对应 starter。
<dependency>
<groupId>io.github.livk-cloud</groupId>
<artifactId>spring-extension-context</artifactId>
<version>${version}</version>
</dependency>
implementation 'io.github.livk-cloud:spring-extension-context:${version}'
implementation("io.github.livk-cloud:spring-extension-context:${version}")
兼容SpringBoot的拓展包
使用spring boot的自动装配特性,自定义配置文件来覆盖官方的配置
spring-boot-extension-autoconfigure 通常由 starter 间接引入。直接引入该模块时,需要自行保证第三方依赖存在,例如
Redisson、Curator、MyBatis、ZXing 等。
3.4.1. starter 快速索引
| 功能 | starter | 说明 |
|---|---|---|
Curator |
curator-spring-boot-starter |
注册 CuratorFramework、CuratorTemplate 和健康检查 |
Disruptor |
disruptor-spring-boot-starter |
扫描事件并注册 SpringDisruptor |
动态数据源 |
dynamic-datasource-spring-boot-starter |
注册 DynamicDatasource 与切换拦截器 |
Excel |
fesod-spring-boot-starter |
注册 Excel 参数解析器和返回值处理器 |
限流 |
limit-spring-boot-starter |
注册限流 AOP 拦截器 |
分布式锁 |
distributed-lock-spring-boot-starter |
注册分布式锁 AOP 拦截器 |
MyBatis |
mybatis-extension-boot-starter |
注册字段注入、SQL 监控等扩展 |
二维码 |
qrcode-spring-boot-starter |
注册二维码请求解析和响应渲染 |
RediSearch |
redisearch-spring-boot-starter |
注册 RedisSearchConnectionFactory 和模板 |
Redisson |
redisson-spring-boot-starter |
注册 RedissonClient |
UserAgent |
browscap-spring-boot-starter / yauaa-spring-boot-starter |
注册 User-Agent 解析器 |
<dependency>
<groupId>io.github.livk-cloud</groupId>
<artifactId>spring-boot-extension-autoconfigure</artifactId>
<version>${version}</version>
</dependency>
implementation 'io.github.livk-cloud:spring-boot-extension-autoconfigure:${version}'
implementation("io.github.livk-cloud:spring-boot-extension-autoconfigure:${version}")
3.4.2. curator
CuratorOperations
Curator相关所有对zookeeper的操作
由CuratorTemplate进行实现
采用CuratorFramework作为底层实现
使用示例
public static void main(String[] args){
CuratorFramework framework = createFramework();
framework.start();
CuratorTemplate template = new CuratorTemplate(framework);
}
curator-spring-boot-starter
artifactId: curator-spring-boot-starter
根据CuratorProperties自动注册CuratorFramework、RetryPolicy、CuratorTemplate以及Curator Actuator
配置
配置前缀为 spring.zookeeper.curator。
spring:
zookeeper:
curator:
connect-string: localhost:2181
base-sleep-time-ms: 50
max-retries: 10
max-sleep-ms: 500
block-until-connected-wait: 10
block-until-connected-unit: seconds
session-timeout: 60s
connection-timeout: 15s
注册 starter 后可直接注入 CuratorFramework 或 CuratorTemplate
使用;Actuator 存在时会注册 Curator 健康检查。
3.4.3. disruptor
Disruptor 组件用于在 Spring 中注册基于 LMAX Disruptor 的事件队列。
通过 @DisruptorScan 扫描事件模型,再通过生产者发送事件,由消费者处理。
DisruptorScan
类似于MyBatisScan用与扫描DisruptorEvent标记的实体
根据实体和DisruptorEvent生成SpringDisruptor<DisruptorEventWrapper<T>>队列
使用示例
@DisruptorScan("com.livk.disruptor")
public class Config{
}
通常在启动类或配置类上声明扫描包:
@SpringBootApplication
@DisruptorScan(basePackages = "com.example.disruptor")
public class DisruptorApplication {
}
DisruptorEventProducer
Disruptor生产者
使用示例
@org.springframework.stereotype.Component
public class DisruptorMqServiceImpl implements DisruptorMqService {
private final DisruptorEventProducer<MessageModel> producer;
public DisruptorMqServiceImpl(SpringDisruptor<MessageModel> disruptor) {
producer = new DisruptorEventProducer<>(disruptor);
}
@Override
public void send(String message) {
log.info("record the message: {}", message);
producer.send(toMessageModel(message));
}
@Override
public void batch(List<String> messages) {
List<MessageModel> messageModels = messages.stream().map(this::toMessageModel).toList();
producer.sendBatch(messageModels);
}
private MessageModel toMessageModel(String message) {
return MessageModel.builder().message(message).build();
}
}
DisruptorEventConsumer
Disruptor消费者
使用示例
@org.springframework.stereotype.Component
public class Consumer implements DisruptorEventConsumer<MessageModel> {
private final ApplicationContext applicationContext;
@Override
public void onEvent(MessageModel wrapper, long sequence, boolean endOfBatch) {
log.info("消费者消费的信息是:{} :{} :{} id:{}", wrapper, sequence, endOfBatch, applicationContext.getId());
}
}
3.4.4. dynamic
动态数据源组件基于 DynamicDatasource 和 AOP 拦截器完成数据源切换。
手动模式可以直接注册 DynamicDatasource,Spring Boot 项目推荐使用 dynamic-datasource-boot-starter
和 @EnableDynamicDatasource。
DynamicDatasource
动态数据源,使用DataSourceContextHolder进行数据源切换
使用示例
@Configuration
public class Config {
@Bean
public DynamicDatasource dynamicDatasource() {
DynamicDatasource dynamicDatasource = new DynamicDatasource();
dynamicDatasource.setTargetDataSources(datasourceMap);
dynamicDatasource.setDefaultTargetDataSource(datasourcePrimary);
return dynamicDatasource;
}
}
dynamic-datasource-boot-starter
artifactId: dynamic-datasource-boot-starter
根据DynamicDatasourceProperties注册DynamicDatasource和DataSourceInterceptor
需要添加EnableDynamicDatasource到Spring Configuration
配置
配置前缀为 spring.dynamic,其中 primary 必须指向 datasource
中已存在的数据源名称。
spring:
dynamic:
primary: mysql
datasource:
mysql:
url: jdbc:mysql://localhost:3306/demo
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
pgsql:
url: jdbc:postgresql://localhost:5432/demo
username: postgres
password: 123456
driver-class-name: org.postgresql.Driver
@EnableDynamicDatasource
@SpringBootApplication
public class DynamicApplication {
}
@DynamicSource 可标记在类或方法上,方法级别通常用于覆盖类级别默认数据源。
3.4.5. fesod
Fesod 组件封装 FastExcel 的导入导出能力,并接入 Spring MVC / WebFlux 方法参数解析和返回值处理。
Excel导入
使用注解 @PostMapping 解析Excel(支持Spring Webflux)
@ExcelParam指定文件名称
parse使用自定义封装fesod的解析器 com.livk.excel.mvc.listener.InfoExcelListener
@RestController
public class InfoController {
@RequestExcel(parse = InfoExcelListener.class)
@PostMapping("uploadList")
public HttpEntity<List<Info>> uploadList(@ExcelParam List<Info> dataExcels) {
return ResponseEntity.ok(dataExcels);
}
@RequestExcel(parse = InfoExcelListener.class)
@PostMapping("upload")
public HttpEntity<List<Info>> upload(@ExcelParam List<Info> dataExcels) {
return ResponseEntity.ok(dataExcels);
}
@RequestExcel(parse = InfoExcelListener.class)
@PostMapping("uploadMono")
public Mono<HttpEntity<List<Info>>> uploadMono(@ExcelParam Mono<List<Info>> dataExcels) {
return dataExcels.map(ResponseEntity::ok);
}
}
@RequestExcel 用于声明上传 Excel 的解析器,@ExcelParam 用于绑定解析后的数据。
WebFlux 场景可以使用 Mono<List<T>> 接收解析结果。
Excel导出
使用注解 @ResponseExcel 或者 @ExcelController 解析Excel(支持Spring
Webflux)
fileName指定下载文件名
suffix指定Excel后缀 默认xlsm
使用ExcelController之后,fileName为out,suffix为xlsm
返回结果为 List<?> Mono<List<?>>
Flux<?> 是sheet名称即为sheet
返回结果为 Map<String,?> Mono<Map<String,?>>
是sheet名称即为map key
@RestController
@RequiredArgsConstructor
public class InfoController {
@ResponseExcel(fileName = "outFile")
@RequestExcel(parse = InfoExcelListener.class)
@PostMapping("uploadDownLoad")
public List<Info> uploadDownLoadMono(@ExcelParam List<Info> dataExcels) {
return dataExcels;
}
@ResponseExcel(fileName = "outFile")
@RequestExcel(parse = InfoExcelListener.class)
@PostMapping("uploadDownLoadMono")
public Mono<List<Info>> uploadDownLoadMono(@ExcelParam Mono<List<Info>> dataExcels) {
return dataExcels;
}
@ResponseExcel(fileName = "outFile")
@RequestExcel(parse = InfoExcelListener.class)
@PostMapping("uploadDownLoadFlux")
public Flux<Info> uploadDownLoadFlux(@ExcelParam Mono<List<Info>> dataExcels) {
return dataExcels.flatMapMany(Flux::fromIterable);
}
}
@ExcelController
public class Info2Controller {
@PostMapping("download")
public Map<String, List<Info>> download(@RequestBody List<Info> dataExcels) {
return dataExcels.stream()
.collect(Collectors.groupingBy(info -> String.valueOf(Long.parseLong(info.getPhone()) % 10)));
}
}
@ResponseExcel 可以标记在方法上;@ExcelController 可以标记在类上,对类内返回结果统一进行
Excel 输出处理。
返回 Map<String, ?> 时,map key 会作为 sheet 名称。
3.4.6. limit
限流组件通过 @Limit 标记方法,并由 LimitInterceptor 拦截执行。
默认提供 RedissonLimitExecutor,适合多实例部署下共享限流状态。
Limit注解
标记方法,用于对此方法进行限流
使用示例
public class UserService {
@Limit(key = "livk:user", rate = 2, rateInterval = 30)
public void save(User user) {
}
}
@Limit 常用属性:
-
key:限流资源 key,建议按业务域命名。 -
rate:单位时间内允许的令牌数。 -
rateInterval:单位时间长度。 -
rateIntervalUnit:时间单位,默认SECONDS。 -
restrictIp:是否按请求 IP 追加限制维度。 -
handler:触发限流后的自定义处理器。
3.4.7. lock
分布式锁组件通过 @DistLock 标记方法,并由 DistributedLockInterceptor
统一执行加锁、释放锁和异常处理。
内置 Redisson 与 Curator 两类实现。
DistLock注解
标记方法,用于对此方法进行分布式锁
使用示例 key支持SpEL表达式,同时支持公平锁、读锁、写锁,并支持异步
在方法上添加 @OnLock(scope = LockScope.DISTRIBUTED_LOCK) (当前分布式锁仅支持Redisson、Curator)+
public class UserService {
@PostMapping("/buy/distributed")
@DistLock(key = "shop")
public void buyLocal(@RequestParam(defaultValue = "2") Integer count) {
}
}
@DistLock 常用属性:
-
key:锁名称,支持表达式生成动态 key。 -
type:锁类型,支持LOCK、FAIR、READ、WRITE。 -
leaseTime:锁租约时间,默认10。 -
waitTime:等待获取锁时间,默认3。 -
async:是否使用异步锁能力。
3.4.8. mybatis
MyBatis 扩展提供 SQL 字段自动注入、JSON 类型处理器和 SQL 执行耗时监控。
内置SQL数据注入工具
@Data
public class User implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private Integer id;
private String username;
@JsonIgnore
@SqlInject(fill = SqlFill.INSERT, supplier = VersionInject.class)
private Integer version;
@JsonFormat(pattern = DateUtils.YMD_HMS, timezone = "GMT+8")
@SqlInject(fill = SqlFill.INSERT, time = InjectType.DATE)
private Date insertTime;
@JsonFormat(pattern = DateUtils.YMD_HMS, timezone = "GMT+8")
@SqlInject(fill = SqlFill.INSERT_UPDATE, time = InjectType.DATE)
private Date updateTime;
}
并且添加com.livk.context.mybatis.SqlDataInjection至MyBatis
@SqlInject 常用属性:
-
fill:填充时机,INSERT只在插入时填充,INSERT_UPDATE在插入和更新时填充。 -
supplier:自定义填充值提供器,推荐使用。 -
time:旧版时间类型填充方式,已废弃,建议迁移到supplier。
数据库JSON类型转换处理(目前只支持MySQL、Postgres)
com.livk.context.mybatis.type.mysql.MysqlJsonTypeHandler
com.livk.context.mybatis.type.postgres.PostgresJsonTypeHandler
添加至MyBatis
3.4.9. qrcode
二维码组件基于 ZXing,支持二维码文本解析和将接口返回值渲染为二维码图片。
同时支持 Spring MVC 与 Spring WebFlux。
二维码导入解析
使用注解 @PostMapping 解析二维码(支持Spring Webflux)
RequestQrCodeText指定文件名称
@RestController
@RequestMapping("qrcode")
public class QRCodeController {
@PostMapping
public Mono<String> textCode(@RequestQrCodeText(fileName = "file") Mono<String> text) {
return text.log();
}
}
文本或者实体导入二维码
使用注解 ResponseQrCode 或者 QrCodeController 解析Excel(支持Spring
Webflux)
@RestController
@RequestMapping("qrcode")
public class QRCodeController {
@ResponseQrCode
@GetMapping
public String text(String text) {
return text;
}
@ResponseQrCode
@GetMapping("mono")
public Mono<String> textMono(String text) {
return Mono.just(text);
}
}
@ResponseQrCode 支持配置二维码尺寸、颜色和图片类型:
@ResponseQrCode(width = 300, height = 300, type = PicType.PNG)
@GetMapping("png")
public String png(String text) {
return text;
}
也可以直接返回 QrCodeEntity 以便在代码中动态设置尺寸和颜色。
3.4.10. redisearch
封装 RedisSearchTemplate、StringRedisSearchTemplate 来操作 RediSearch。
自动配置基于 lettuce-mod 创建 RedisSearchConnectionFactory,并在存在 Jackson 时提供 JSON
序列化的模板。
配置
配置前缀为 spring.redisearch,默认连接 localhost:6379 的 0
号库。
spring:
redisearch:
host: localhost
port: 6379
database: 0
username:
password:
ssl: false
timeout: 3s
client-name: redisearch-client
pool:
enabled: true
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
集群模式可启用 cluster 配置:
spring:
redisearch:
cluster:
enabled: true
nodes:
- 127.0.0.1:6379
- 127.0.0.1:6380
max-redirects: 3
使用模板
RedisSearchTemplate<String, Object> 适合对象读写,StringRedisSearchTemplate
适合字符串场景。
如果需要调整 lettuce 的底层资源,可以注册 ClientResourcesBuilderCustomizer。
@Service
public class ArticleSearchService {
private final StringRedisSearchTemplate StringRedisSearchTemplate;
public ArticleSearchService(StringRedisSearchTemplate StringRedisSearchTemplate) {
this.StringRedisSearchTemplate = StringRedisSearchTemplate;
}
}
3.4.11. redisson-spring-boot-starter
artifactId: redisson-spring-boot-starter
适配 Spring Boot 的 yaml、properties 配置文件,使用 Redisson 原生 Config
模型完成配置绑定。
自动配置会注册 RedissonClient,并优先读取 spring.redisson.config 下的
Redisson 配置;未配置时会回退到 Spring Boot Redis 单机配置创建客户端。
单机配置
spring:
redis:
host: localhost
port: 6379
password:
redisson:
config:
single-server-config:
address: redis://127.0.0.1:6379
database: 0
connection-pool-size: 64
connection-minimum-idle-size: 24
集群配置
spring:
redisson:
config:
cluster-servers-config:
node-addresses:
- redis://127.0.0.1:6379
- redis://127.0.0.1:6380
scan-interval: 2000
使用
引入 starter 后可直接注入 RedissonClient,分布式锁、限流等组件也可以复用同一个客户端。
@Service
public class LockService {
private final RedissonClient redissonClient;
public LockService(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
public void lock() {
RLock lock = redissonClient.getLock("demo:lock");
lock.lock();
try {
// business
}
finally {
lock.unlock();
}
}
}
3.4.12. sequence
发号器,可自定义datasource、redis、snowflake等发号策略
datasource发号器内置Postgres、Mysql、H2等数据库发号策略
未支持的数据库实现 com.livk.context.sequence.support.db.SequenceDbHelper 并注册到
META-INF/spring.factories 文件
发号模式
-
Sequence.snowflake(dataCenterId, machineId):本地雪花算法,不依赖外部存储。 -
DbRangeManager:基于数据库分段取号,内置 MySQL、PostgreSQL、H2。 -
RedisRangeManager:基于 RedisINCRBY分段取号,可使用 Lettuce 或 Spring Data Redis 连接。
SequenceBuilder 支持设置:
-
bizName:业务名称,必填,不同业务使用不同号段。 -
step:每次申请的号段步长,默认1000。 -
stepStart:初始值,默认0,真实发号从stepStart + 1开始。
@Configuration
public class Config{
@Bean
public Sequence dbSequence(DataSource dataSource) {
Sequence sequence = SequenceBuilder.builder(new DbRangeManager(dataSource)).bizName("test-sequence").build();
return sequence;
}
@Bean
public Sequence lettuceSequence(RedisClient redisClient) {
LettuceSequenceRedisHelper helper = new LettuceSequenceRedisHelper(redisClient);
Sequence sequence = SequenceBuilder.builder(new RedisRangeManager(helper)).bizName("test-sequence").build();
return sequence;
}
@Bean
public Sequence redisSequence(RedisConnectionFactory factory) {
SpringSequenceRedisHelper helper = new SpringSequenceRedisHelper(factory);
Sequence sequence = SequenceBuilder.builder(new RedisRangeManager(helper)).bizName("test-sequence").build();
return sequence;
}
@Bean
public Sequence snowflakeSequence() {
Sequence sequence = Sequence.snowflake(1, 2);
return sequence;
}
}
3.4.13. useragent
提供 User-Agent 解析能力,支持 Spring MVC 与 Spring WebFlux。
通过 @UserAgentInfo 注入解析后的 UserAgent,也可以从上下文持有器中获取当前请求的结果。
MVC 使用
@RestController
@RequestMapping("user-agent")
public class UserAgentController {
@GetMapping
public HttpEntity<Map<String, UserAgent>> get(@UserAgentInfo UserAgent userAgent) {
Map<String, UserAgent> map = Map.of("argument", userAgent,
"context", UserAgentContextHolder.getUserAgentContext());
return ResponseEntity.ok(map);
}
}
WebFlux 使用
@RestController
@RequestMapping("user-agent")
public class ReactiveUserAgentController {
@GetMapping
public Mono<UserAgent> get(@UserAgentInfo Mono<UserAgent> userAgent) {
return userAgent;
}
}
3.5. spring testcontainers support容器拓展
提供自定义testcontainers与spring boot的支持
<dependency>
<groupId>io.github.livk-cloud</groupId>
<artifactId>spring-testcontainers-support</artifactId>
<version>${version}</version>
<scope>test</scope>
</dependency>
testImplementation 'io.github.livk-cloud:spring-testcontainers-support:${version}'
testImplementation("io.github.livk-cloud:spring-testcontainers-support:${version}")
3.5.1. DockerImageNames
提供常用容器镜像名称构造方法:
-
DockerImageNames.mysql() -
DockerImageNames.postgres() -
DockerImageNames.redis() -
DockerImageNames.redisStack() -
DockerImageNames.zookeeper()
每个方法都提供默认 latest 标签,也可以传入指定 tag。
4. 问题
4.1. 常见问题
4.1.1. 为什么 starter 引入后没有生效
请先确认已经引入对应 starter,而不是只引入了 spring-extension-context。
spring-extension-context 提供基础 API,starter 才会引入对应自动配置模块并注册 Spring
Bean。
4.1.2. 自动配置文件没有生成
spring-auto-service 是编译期注解处理器,需要同时加入 compileOnly 和 annotationProcessor。
如果使用 Kotlin,需要按项目的 Kotlin KAPT/KSP 配置接入注解处理器。
4.1.3. Redisson、Curator、Redisearch 连接失败
先检查配置前缀是否正确:
-
Redisson:
spring.redisson -
Curator:
spring.zookeeper.curator -
RediSearch:
spring.redisearch
再确认中间件地址、端口、认证信息与本地网络环境。使用 Testcontainers 时,可优先使用 @ServiceConnection
或测试配置注入连接信息。