# 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` 级别打印日志