# commons
**Repository Path**: jarvis-lib/commons
## Basic Information
- **Project Name**: commons
- **Description**: Spring Boot 、 Spring Cloud 整合工具类
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 4
- **Forks**: 2
- **Created**: 2019-03-06
- **Last Updated**: 2025-12-17
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Tony Stark
向我的偶像,最牛程序员 **Tony Stark(1970-2019)** 致敬!
# 使用
1. 在 pom.xml 中添加仓库
```xml
eastsoft-snapshots
Eastsoft Snapshots
http://218.58.62.115:18081/nexus/repository/snapshots/
true
```
2. 在 pom.xml 中添加坐标
```xml
com.stark.commons
commons-lang
1.1.0-SNAPSHOT
com.stark.commons
commons-spring-core
1.1.0-SNAPSHOT
com.stark.commons
commons-spring-web
1.1.0-SNAPSHOT
```
# 功能
- [commons-lang](#)
- [jdbc连接数据库](commons-lang/src/main/java/com/stark/commons/lang/jdbc/DBAccess.java)
- [AES加解密](commons-lang/src/main/java/com/stark/commons/lang/security/AES.java)
- [日期工具类](commons-lang/src/main/java/com/stark/commons/lang/util/DateUtil.java)
- [POI操作Excel工具类](commons-lang/src/main/java/com/stark/commons/lang/util/ExcelUtil.java)
- [文件操作工具类](commons-lang/src/main/java/com/stark/commons/lang/util/FileUtils.java)
- [FTP客户端](commons-lang/src/main/java/com/stark/commons/lang/util/FtpClient.java)
- [http(s)请求](commons-lang/src/main/java/com/stark/commons/lang/util/HttpUtil.java)
- [数字处理工具类](commons-lang/src/main/java/com/stark/commons/lang/util/NumberUtils.java)
- [PDF工具类](commons-lang/src/main/java/com/stark/commons/lang/util/PdfUtil.java)
- [常用方法](commons-lang/src/main/java/com/stark/commons/lang/util/Utils.java)
- [WORD工具类](commons-lang/src/main/java/com/stark/commons/lang/util/WordUtil.java)
- [XML工具类](commons-lang/src/main/java/com/stark/commons/lang/util/XmlUtil.java)
- [commons-spring-core](#commons-spring-core)
- [多数据源路由(*)](#多数据源路由)
- [事务切面(*)](#事务切面)
- [Atomikos JTA 分布式事务(*)](#atomikos-jta-分布式事务)
- [Elasticsearch 整合 X-Pack(*)](#elasticsearch-整合-x-pack)
- [Elasticsearch 整合 Search Guard(*)](#elasticsearch-整合-search-guard)
- [日志切面(*)](#日志切面)
- [线程池](#线程池)
- [通用缓存接口(*)](#通用缓存接口)
- [MinIO(*)](#minio)
- [commons-spring-web](#commons-spring-web)
- [eureka](#eureka)
- [事件监听(*)](#事件监听)
- [feign](#feign)
- [携带请求头(*)](#携带请求头)
- [错误码标记业务异常不降级(*)](#错误码标记业务异常不降级)
- [支持分页(*)](#支持分页)
- [zuul](#zuul)
- [uri 接口级别超时配置(*)](#uri-接口级别超时配置)
- [全局异常处理(*)](#全局异常处理)
- [跨域(*)](#跨域)
- [安全(*)](#安全)
- [性能监控(*)](#性能监控)
# commons-spring-core
## 多数据源路由
### 配置
1. `yml` 中指定 `spring.datasource.multiple=true` ,开启自动配置
```yml
spring:
datasource:
multiple: true
```
2. `yml` 中配置多数据源
```yml
spring:
datasource:
druid:
ds1:
driver-class-name: net.sourceforge.jtds.jdbc.Driver
url: jdbc:jtds:sybase://129.1.50.194:8888/escloud;charset=cp936
username: escloud
password: fpSPcx5kfoNl+aZalQQcaJq0WQ3OkikNzYjBpUquEQEpCEg6AxJZWZMyts1YPIn97PYD3843FQh8j/7xdSE7Eg==
initial-size: 10
min-idle: 10
max-active: 20
keep-alive: true
validation-query: select count(*) from S_COURT
validation-query-timeout: 0
filters: config,stat,slf4j
connection-properties: config.decrypt=true;config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIxnQJmAfL8vCTtuKViDH5FVEPC5Ao1gNOsBHgv+e6ccWV5CLJoAy1+pZPW5RWGCFYikTg6Q11oCUuC6kJAW0GcCAwEAAQ==
ds2:
driver-class-name: net.sourceforge.jtds.jdbc.Driver
url: jdbc:jtds:sybase://129.1.50.194:8888/escourt6;charset=cp936
username: escourt6
password: fpSPcx5kfoNl+aZalQQcaJq0WQ3OkikNzYjBpUquEQEpCEg6AxJZWZMyts1YPIn97PYD3843FQh8j/7xdSE7Eg==
initial-size: 10
min-idle: 10
max-active: 20
validation-query: select count(*) from S_COURT
validation-query-timeout: 0
filters: config,stat,slf4j
connection-properties: config.decrypt=true;config.decrypt.key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIxnQJmAfL8vCTtuKViDH5FVEPC5Ao1gNOsBHgv+e6ccWV5CLJoAy1+pZPW5RWGCFYikTg6Q11oCUuC6kJAW0GcCAwEAAQ==
web-stat-filter:
enabled: true
url-pattern: /*
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
stat-view-servlet:
enabled: true
url-pattern: /druid/*
login-username: eastsoft
login-password: eastsoft.cn
```
3. 实现 `com.stark.commons.spring.core.support.sql.RoutingDataSource` 接口,返回数据源 bean
```java
@Configuration
public class DataSourcesConfig {
@Bean
@ConfigurationProperties("spring.datasource.druid.ds1")
public RoutingDataSource escloudDataSource() {
DruidDataSource druidDataSource = DruidDataSourceBuilder.create().build();
return new DruidRoutingDataSource(druidDataSource, "ds1", true);
}
@Bean
@ConfigurationProperties("spring.datasource.druid.ds2")
public RoutingDataSource esshare6DataSource() {
DruidDataSource druidDataSource = DruidDataSourceBuilder.create().build();
return new DruidRoutingDataSource(druidDataSource, "ds2", false);
}
}
```
### 通过 `@DataSource("lookupKey")` 注解切换数据源
- 方法级别,方法执行前将切换至目标数据源
```java
public class DataSourcesConfig {
@DataSource("ds2")
public void function() {
// ...
}
}
```
- 类级别,类中所有的方法执行前都将切换至目标数据源
```java
@DataSource("ds2")
public class DataSourcesConfig {
public void function1() {
// ...
}
public void function2() {
// ...
}
}
```
### 注意
- 支持 **单一数据源** 事务,不支持 **跨数据源** 事务(分布式事务);
- 多数据源分布式事务请参考 [Atomikos JTA 分布式事务](#atomikos-jta-分布式事务)
## 事务切面
### `yml` 配置
```yml
spring:
transaction:
aop:
base-packages: com.stark.demo.service.impl # 显示指定包扫描位置,开启自动配置
read-only-methods: get*,find*,query*,select*,count* # 只读事务方法,支持通配符,多个以 "," 隔开,默认值 ""
required-methods: insert*,add*,save*,update*,delete*,remove* # 写事务方法,支持通配符,多个以 "," 隔开,默认值 "*"
timeout: -1 # 超时回滚时间,默认值 -1 不回滚
```
### 事务注解优先
- 有 `@Transactional` 和 `@NonTransactional` 注解的方法,事务按照注解的逻辑执行,不受切面控制
- `@NonTransactional` 等同于 `@Transactional(propagation = Propagation.NOT_SUPPORTED)`
### 事务时间开销
- 事务开启关闭一次大约需要 `10ms` 的时间
- 非写事务和必要的只读事务,尽量通过 `@NonTransactional` 或 `@Transactional(propagation = Propagation.NOT_SUPPORTED)` 注解关闭事务
- 建议按照 `spring.transaction.aop.read-only-methods` 和 `spring.transaction.aop.required-methods` 的约定定义方法名
## Atomikos JTA 分布式事务
### 配置
1. 引入 `spring-boot-starter-jta-atomikos` 包;
```xml
org.springframework.boot
spring-boot-starter-jta-atomikos
${springboot.version}
```
2. `yml` 中指定 `spring.jta.enabled=true` ,开启自动配置
```yml
spring:
jta:
enabled: true # 显示指定 true ,开启自动配置
log-dir: ./ # 日志文件目录
```
### 注意
- 事务切面、注解两种方式的事务均为 **分布式事务** ,分组事务需要通过事务的传播属性来设计实现,见[分组事务](#分组事务)
- 开启事务切面后,repository 中自定义的增、删、改方法,需要手动添加 `@Transactional` 注解
### 分组事务
- 业务场景
用户 user 、用户信息 userInfo 在一个事务,部门 dept 在一个事务,两个事务相互独立。
- 代码设计
```java
@Service("userService")
public class UserService {
@Autowired
private UserInfoService userInfoService;
@Autowired
private DeptService deptService;
// 事务切面织入事务
public void add(User user, UserInfo userInfo, Dept dept) {
// 通过 @Transactional(propagation = Propagation.REQUIRES_NEW) 新起事务
userInfoService.addUserAndUserInfo(user, userInfo);
// 继承父方法的事务
deptService.add(dept);
}
}
@Service("userInfoService")
public class UserInfoService {
@Autowired
private UserRepository userRepository;
@Autowired
private UserInfoRepository userInfoRepository;
// 新起一个事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addUserAndUserInfo(User user, UserInfo userInfo) {
userRepository.save(user);
userInfoRepository.save(userInfo);
}
}
@Service("deptService")
public class DeptService {
@Autowired
private DeptRepository deptRepository;
// 事务切面织入事务
public void add(Dept dept) {
deptRepository.save(dept);
}
}
```
## Elasticsearch 整合 X-Pack
### 版本
Elasticsearch 6.8 和 7.1 以上。
### 配置
1. 引入依赖
```xml
org.springframework.boot
spring-boot-starter-data-elasticsearch
${springboot.version}
org.elasticsearch.client
transport
org.elasticsearch.client
x-pack-transport
${elasticsearch.version}
```
2. `yml` 中配置:
```yml
spring:
data:
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: es1.stark.com:9300,es2.stark.com:9300
xpack:
security-transport-ssl-enabled: true
security-user: elastic
security-password: 123456
ssl-verification-mode: certificate
ssl-keystore-path: classpath:certs/dev-qd/elastic-certificates.p12
ssl-truststore-path: classpath:certs/dev-qd/elastic-certificates.p12
ssl-keystore-password: 123456
ssl-truststore-password: 123456
```
## Elasticsearch 整合 Search Guard
### 配置
1. 引入依赖;
```xml
org.springframework.boot
spring-boot-starter-data-elasticsearch
${springboot.version}
com.floragunn
search-guard-ssl
${elasticsearch.version}-25.6
```
2. `yml` 中配置:
```yml
spring:
data:
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: es1.stark.com:9300,es2.stark.com:9300
search-guard:
ssl-transport-pemtrustedcas-filepath: classpath:elk/dev/root-ca.pem
ssl-transport-pemcert-filepath: classpath:elk/dev/elk-server.pem
ssl-transport-pemkey-filepath: classpath:elk/dev/elk-server.key
ssl-transport-pemkey-password: 123456
```
### 注意
- `elasticsearch` 、 `transport` 、 `search-guard-ssl` 三者版本**必须一致**!
## 日志切面
1. 在应用配置类中增加 `@EnableAspectJAutoProxy` 开启切面注解支持;
2. 在应用系统中实现 `com.stark.commons.spring.core.support.log.LogService` 接口,开启日志切面自动配置;
3. 在方法上添加 `@Log(type = {int}, businessKey = "{String}", message = "{String}")` 注解, `String` 类型参数支持 `spel` 表达式。
```java
@Service
public class UserService {
@Log(type = LogType.TABLE_INSERT, businessKey = "#user.id", message = "'新增用户:id=' + #user.id")
public User add(User user) {
// ...
}
@Log(type = LogType.TABLE_DELETE, businessKey = "#id", message = "'删除用户:id=' + #id")
public int delete(Long id) {
// ...
}
@Log(type = LogType.TABLE_UPDATE, businessKey = "#user.id", message = "'修改用户:id=' + #user.id")
public User update(User user) {
// ...
}
@Log(type = LogType.TABLE_SELECT, businessKey = "#id", message = "'查询用户:id=' + #p0")
public User get(Long id) {
// ...
}
}
```
## 线程池
`yml` 配置
```yml
spring:
task:
execution: # 任务执行器,多线程操作使用
thread-name-prefix: task- # 线程名前缀,打印日志时使用
pool:
core-size: 8 # 核心线程数,默认 8
max-size: 2147483647 # 最大线程数,默认 Integer.MAX_VALUE
queue-capacity: 2147483647 # 队列容量,默认 Integer.MAX_VALUE
keep-alive: 60s # 空闲线程存活时间
allow-core-thread-timeout: true # 是否保持 core-size 数
scheduling: # 任务调度器, job 执行时使用
thread-name-prefix: scheduling- # 线程名前缀,打印日志时使用
pool:
size: 1 # 线程数,默认 1 同时只能运行一个 job 作业
```
## 通用缓存接口
### 目的
提供一套通用的缓存接口,在某些场景下,比通过 `@Cacheable` 系列注解的方式更方便的使用缓存。
### 内置实现
- `redis`
- `ehcache`
### 使用方法
自动注入 `com.stark.commons.spring.core.support.cache.CacheService`
```java
@Autowired
private CacheService cacheService;
```
## MinIO
1. `yml` 配置
```yml
spring:
minio:
endpoint: http://192.168.22.100:9000 # 服务器地址,集群需提供一个代理地址
port: 9000 # 端口
secure: false # 是否使用 https 协议
access-key: minio # 用户名
secret-key: 12345678 # 密码
region: china-shandong-1 # 区域
```
2. 代码中自动注入 `io.minio.MinioClient` 并使用。
# commons-spring-web
## eureka
### 事件监听
1. 开启监听
```yml
eureka:
server:
enable-registered-event-listener: true # 服务上线(注册)事件监听,默认 true ,以 WARN 级别打印日志
enable-renewed-event-listener: true # 服务续约事件监听,默认 true ,以 INFO 级别打印日志
enable-canceled-event-listener: true # 服务下线监听,默认 true ,以 WARN 级别打印日志
```
2. 自定义事件处理
- 实现 `com.stark.commons.spring.web.support.eureka.RegisteredEventHandler` 接口,定义上线事件处理器
```java
@Component
public class MyRegisteredEventHandler implements RegisteredEventHandler {
@Override
public void handle(EurekaInstanceRegisteredEvent event) {
if (!event.isReplication()) {
// TODO: 业务逻辑
}
}
}
```
- 实现 `com.stark.commons.spring.web.support.eureka.RenewedEventHandler` 接口,定义续约事件处理器
```java
@Component
public class MyRenewedEventHandler implements RenewedEventHandler {
@Override
public void handle(EurekaInstanceRenewedEvent event) {
if (!event.isReplication() && event.getInstanceInfo() != null) {
// TODO: 业务逻辑
}
}
}
```
- 实现 `com.stark.commons.spring.web.support.eureka.CanceledEventHandler` 接口,定义下线事件处理器
```java
@Component
public class MyCanceledEventHandler implements CanceledEventHandler {
@Override
public void handle(EurekaInstanceCanceledEvent event) {
if (!event.isReplication()) {
// TODO: 业务逻辑
}
}
}
```
## feign
### 携带请求头
```yml
feign:
headers-include: Authorization # 多个以 "," 隔开,将从 request 中复制请求头
```
### 错误码标记业务异常不降级
```yml
feign:
decode-codes:
400: # 为空代表取 exception.message
404: # 为空代表取 exception.message
401: '未登录或登录超时'
403: '没有权限'
499: '客户端关闭连接'
```
### 支持分页
1. `org.springframework.data.domain.Pageable` 和 `org.springframework.data.domain.Sort` 作为接口参数时,不要加 `@SpringQueryMap` 注解;
2. 自动配置的 `com.stark.commons.spring.web.support.page.PageCombinedSerializer` 负责 `org.springframework.data.domain.Page` 的序列化和反序列化。
## zuul
### uri 接口级别超时配置
```yml
zuul:
routes:
manager:
service-id: escloud-service-manager
path: /manager/**
route-timeout:
- service-id: escloud-service-manager # 微服务 ID
uri-timeout:
- uri: /user # URI
method: post # 请求方法,默认 get
connect-timeout: 1000 # 连接超时毫秒数,默认 1000
read-timeout: 3000 # 读取数据超时毫秒数,默认 1000
- uri: /user/{id}
method: get
connect-timeout: 1000
read-timeout: 1000
- uri: /user/{id}
method: put
connect-timeout: 1000
read-timeout: 3000
```
## 全局异常处理
### 内置的异常处理器
- `com.stark.commons.spring.web.support.exception.BindExceptionHandler`
处理 `org.springframework.validation.BindException` 异常
- `com.stark.commons.spring.web.support.exception.ConstraintViolationExceptionHandler`
处理 `javax.validation.ConstraintViolationException` 异常
- `com.stark.commons.spring.web.support.exception.HttpMessageConversionExceptionHandler`
处理 `org.springframework.http.converter.HttpMessageConversionException` 异常
- `com.stark.commons.spring.web.support.exception.HttpRequestMethodNotSupportedExceptionHandler`
处理 `org.springframework.web.HttpRequestMethodNotSupportedException` 异常
- `com.stark.commons.spring.web.support.exception.InvalidGrantExceptionHandler`
处理 `org.springframework.security.oauth2.common.exceptions.InvalidGrantException` 异常
- `com.stark.commons.spring.web.support.exception.MethodArgumentNotValidExceptionHandler`
处理 `org.springframework.web.bind.MethodArgumentNotValidException` 异常
- `com.stark.commons.spring.web.support.exception.MethodArgumentTypeMismatchExceptionHandler`
处理 `org.springframework.web.method.annotation.MethodArgumentTypeMismatchException` 异常
- `com.stark.commons.spring.web.support.exception.ServletRequestBindingExceptionHandler`
处理 `org.springframework.web.bind.ServletRequestBindingException` 异常
**注意:** 继承内置异常处理器并注入 IOC 容器,可覆盖默认的异常处理器。
### 自定义异常处理器
- 实现 `com.stark.commons.spring.web.support.exception.ExceptionHandler` 接口,处理指定异常,如 `KnownException`
```java
public class KnownExceptionHandler implements ExceptionHandler {
/**
* 当前异常处理器是否可以处理传入的异常。
* @param ex 异常对象。
* @return 可以处理异常返回 {@literal true} ,否则返回 {@literal false} 。
*/
@Override
public boolean instanceofException(Exception ex) {
return ex instanceof KnownException;
}
/**
* 处理异常并返回响应内容。
* @param ex 异常对象。
* @return 响应内容,包含状态码、异常信息。
*/
@Override
public ResponseEntity handle(Exception ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(((KnownException) ex).getMessage());
}
}
```
- 把异常处理器注入 IOC 容器
```java
@Configuration
public class MyConfig {
@Bean
public KnownExceptionHandler knownExceptionHandler() {
return new KnownExceptionHandler();
}
}
```
### 自定义异常处理
- 自定义的异常继承 `com.stark.commons.lang.exception.KnownException`
```java
public class UserNotFoundException extends KnownException {
// ...
}
```
- 内置处理器将返回 `400` 状态码和异常信息
### 未知的异常将返回 `500` 状态码和异常信息
## 跨域
### `yml` 配置
```yml
web:
cors:
enabled: false # 是否允许跨域,默认 false
path-pattern: /** # 允许的请求路径,默认 /**
allowed-origins: '*' # 允许的域名,默认 *
allowed-methods: '*' # 允许的请求方式,默认 *
max-age: 1800 # 允许客户端缓存响应头有效时间,单位秒,默认 1800
```
## 安全
### `yml` 配置
```yml
web:
security-check: true
```
### 功能
- 防止 sql 注入
- 防止 xss 攻击
### 注意
- 会降低性能
## 性能监控
### `yml` 配置
```yml
web:
monitor-standard: 3000 # 响应标准时间,单位毫秒
```
### 功能
- controller 响应时间超过标准值(默认 3s),以 `warn` 级别打印日志