细说spring-boot-x
特别说明:通常,我们会将
SpringBoot
写作SpringBoot
,而此处写作spring-boot
是因项目的确就叫做spring-boot-x
。以下简称项目。
项目地址:https://github.com/OpeningO/spring-boot-x
源起
娱坛有云一人一首成名曲,虽互联网技术与娱之文艺不同,但个人仍以为技术与艺类同,皆为高雅之事。故成此项目,是以个人有利器,便于而后开疆扩土。
总览
更新说明
- 支持分布式锁、幂等处理 [2021.8.16更新] since v5.1.0
- 重构整个包,发布4.0.0.RELEASE版本 [ 2021.6.30更新 ]
- 优化分布式id生成器,隔离默认的redis配置及Zookeeper配置,让id生成与业务完全分离 [ 2021.6.30更新 ] since v3.2.0.RELEASE
特性清单
-
手动事务管理 [2021.6.29更新]
-
分布式id生成器gedid,DidLoader [ 2021.6.25更新 ]
-
Safety工具 [ 2021.6.25更新 ] [merge to jdkits since v5.1.0]
-
请求日志,包括请求源、请求目标、请求参数、处理时间、错误异常等信息;
-
请求响应参数的自动装配(映射);
-
跨域的配置;
-
嵌入
SpringBoot
的异常处理机制,可以将原来的错误信息中插入其他信息、或将其解析或转换为其他信息; -
如
SpringBoot
之starter
动态装配或在yml
中配置相关特性; -
简化的
Redis
操作; -
提炼
Elasticsearch
之HighlevelClient
常用操作; -
feign
的请求头参数的处理:合并上下游的请求头参数,并发场景的数据处理策略; -
基于
Druid
和Hikari
的动态路由RoutingDataSource
; -
SpringBoot
应用的配置信息的自动拷贝; -
更多继续完善… …
-
引入依赖
1 2 3 4 5
<dependency> <groupId>org.openingo.boot</groupId> <artifactId>spring-boot-x</artifactId> <version>${spring-boot-x.version}</version> </dependency>
细说特性
分布式锁
基于redis实现
|
|
幂等处理
提供了两个注解
-
org.openingo.spring.boot.extension.idempotent.annotation.Idempotent
用于指定某方法是否需要幂等处理;1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
@Documented @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Idempotent { /** * the key spring el */ String keyEl() default ""; /** * expire minutes */ long expireMinutes() default 5L; }
-
org.openingo.spring.boot.extension.idempotent.annotation.IdempotentKey
用于从方法参数中指定幂等标识;1 2 3 4 5
@Documented @Target({ElementType.PARAMETER, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface IdempotentKey { }
-
基于redis实现
|
|
手动事务管理更新 [2021.8.16] since 5.1.0
|
|
分布式Id生成器 [ 2021.6.25更新 ] since 3.0.0.RELEAE
从这里看分布式id使用说明
Safety 工具 [ 2021.6.25更新 ] since 3.0.0.RELEAE
已合并到jdkits,since v5.1.0
封装了
ReentrantLock
,具体查看源码org.openingo.spring.safety.Safety
|
|
请求日志
先看看效果
|
|
以上请求是从Postman发起的一个请求,对此请求的各类参数都进行了一一罗列:
- 源(Client IP);
- 发起时间(Request Time);
- 请求的handler(对应的Controller、Action);
- 请求方式(Method);
- 处理时间(Processing Time);
- 请求头(Header);
- 请求体(Body);
- 参数(Parameters);
- 此次请求出现的异常(如没有异常此项自然就没有了)。
如何使用?
-
引入项目依赖及
spring-boot-starter-aop
,在启动类上加入@EnableExtension
即可;1 2 3 4 5 6 7 8 9 10 11 12 13 14
/** * App * * @author Qicz */ @SpringBootApplication @EnableExtension public class App { public static void main(String[] args) throws InterruptedException { SpringApplicationX.run(App.class, args); SpringApplicationX.applicationInfo(); } }
-
可配置?
1 2 3 4 5 6 7 8 9
openingo: http: request: cors: allowed-header: "*" enable: true allowed-all: true log: enable: true
如上即可对log及cors进行配置。
请求响应参数的自动装配(映射)
通常情况下,我们会将后端处理的结果按照固定的格式包装之后,再返回给前端,所以在Controller
需要对处理结果进行统一的包装处理,于是有了下面的这种代码:
|
|
这种统一的包装处理,既然是统一行为,那么必然是可以进行自动的统一的处理方式。于是有了这个请求响应参数的自动装配(映射)
。先看看使用了这个特性之后,我们的代码变成了什么样:
|
|
可以看到,我们添加了一个@AutoMappingRespResult
注解,在方法体,并无什么特别的了。
如何使用?
- 引入项目依赖,在启动类上加入
@EnableExtension
; - 在需要包装的
Controller
上加入@AutoMappingRespResult
即可。
特别说明
使用@AutoMappingRespResult
后都将使用org.openingo.jdkits.http.RespData
进行数据包装。而org.openingo.jdkits.http.RespData
是可以对返回的数据进行动态配置的。默认情况下,返回sc
、sm
、data
,如果需要可以使用org.openingo.jdkits.http.RespData.Config
修改它们为其他任意值。
嵌入SpringBoot的异常处理机制
SpringBoot
的错误处理是借助org.springframework.boot.web.servlet.error.DefaultErrorAttributes
进行的,常看到的信息如下:
|
|
进行项目的嵌入异常处理后,可以得到以下的信息:
|
|
如何使用?
-
引入项目依赖,在启动类上加入
@EnableExtension
,即可使用; -
可配置?
1 2 3 4
openingo: http: error: enable: true
如上即可对error进行配置。
扩展异常处理
默认情况下项目使用org.openingo.spring.http.request.error.DefaultServiceErrorAttributes
提供异常嵌入处理,可以扩展其对异常进行拓展处理,如下:
|
|
简化的Redis操作
默认情况下对redis
的操作需要使用redisTemplate
提供的繁琐操作,各种opsFor...
,使用简化方式之后业务代码中会将此类统统干掉,而且将大部分的操作都按照redis官方的command的方式进行了对齐,更容易理解和使用它们。先来看看使用的效果:
|
|
在业务中,通常我们会对写入redis
的数据进行region的处理,也就是从key
的角度对数据进行划分,以上看到的KeyNamingKit
就是进行了这样的操作。当然了,仅仅使用KeyNamingKit
是不能完成这个功能的,还需要定义一个keyNamingPolicy
,当然项目已提供了一个默认的keyNamingPolicy
|
|
如何使用?
-
引入项目依赖及
spring-boot-starter-data-redis
,在启动类上加入@EnableExtension
,即可使用; -
可配置?
1 2 3
openingo: redis: enable: true
自定义keyNamingPolicy
按照如下即可
|
|
在操作前使用KeyNamingKit.set
将当前操作的region
写入,如此处的openingo
|
|
Elasticsearch之HighlevelClient常用操作
常用操作即
- saveOrUpdate类
- delete类
- search类:分页处理,随机推荐等等
- 同步处理、异步处理
如下:
|
|
如何使用?
- 引入项目依赖及
spring-boot-starter-data-elasticsearch
及elasticsearch-rest-high-level-client
,在启动类上加入@EnableExtension
,即可使用。
基于Druid和Hikari的动态路由RoutingDataSource
spring官方提供了org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
用于支持动态路由,但其对数据源本身的处理很少,故基于此对其从根上进行了扩展,于是有了org.openingo.spring.datasource.routing.RoutingDataSource
,可以便捷的加入移除关闭数据源,且支持Druid
及Hikari
。
如何使用?
-
引入项目依赖(使用
druid
时加入其依赖),在启动类上加入@EnableExtension
,即可使用; -
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
/** * DataSourceService * * @author Qicz */ @Service @Slf4j public class DataSourceService implements IDataSourceService { @Autowired RoutingDataSource routingDataSource; @Autowired DruidDataSource dataSource; @Override public void switchDataSource(String name) throws SQLException { try { System.out.println("======before======"+name); routingDataSource.getConnection(); System.out.println(routingDataSource.getCurrentUsingDataSourceProvider().hashCode()); RoutingDataSourceHolder.setCurrentUsingDataSourceKey(name); routingDataSource.getConnection(); System.out.println("======after======"); System.out.println(routingDataSource.getCurrentUsingDataSourceProvider().hashCode()); } finally { RoutingDataSourceHolder.clearCurrentUsingDataSourceKey(); } } @Override public void add(String name) { //routingDataSource.setAutoCloseSameKeyDataSource(false); DruidDataSourceProvider druidDataSourceProvider = new DruidDataSourceProvider(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword()); druidDataSourceProvider.startProviding(); routingDataSource.addDataSource(name, druidDataSourceProvider); } }
SpringBoot应用的配置信息的自动拷贝
通常情况下,我们的SpringBoot
应用都会有各种的配置文件或yaml
或properties
,而在项目部署时有需要将他们进行外部化,便于动态配置,项目的此特性就基于此而实现。
如何使用?
-
引入项目依赖;
-
在启动类中使用
SpringApplicationX
,如下:1 2 3 4 5 6 7 8 9 10 11 12 13
/** * App * * @author Qicz */ @SpringBootApplication public class App { public static void main(String[] args) throws InterruptedException { SpringApplicationX.run(App.class, args); SpringApplicationX.applicationInfo(); } }
若仅拷贝资源,使用
java -jar xx.jar ccp
即可。
总结
以上对项目的现有的核心功能进行了简单说明,具体可查看项目源码或待后续在针对性的详细说明实现思路。
此项目在设计之初,经过了多番反复的调整,才有了现在的模样,在打磨过程中,对SpringBoot
又进一步的加深的了解,提升了许多。接下来,会继续将常见的功能不断的完善和加入。