---

---

# Elab-Spring

该项目用来针对一些Spring的拓展做一些统一的封装使得在使用上非常方便

这里只是大概介绍一下功能的使用。

直接使用的话

```java
// 如果有相应的配置文件参数,需要将配置文件注入到里面来
@PropertySource(value = {"classpath:database.properties","classpath:autoConfig.properties"},encoding = "UTF-8")
// 直接导入相关的配置类
@Import({SpringCommonConfig.class, DataSourceConfigBean.class, JdbcBeanConfig.class, TransactionConfigBean.class})

```

------

[TOC]



## 功能介绍

### SpringCommonConfig：基本的Spring相关的配置

封装了**ClientHttpRequestFactory**、**RestTemplate**、**PropertyPlaceholderConfigurer**等类.

有一些可变的参数可以再配置文件中指定，这里面的数据都是默认值，可以不填写
```perl
# httpClient连接数
httpClient.connect.timeOut=120000
# httpClient超时时间
httpClient.read.timeOut=120000
# 读取property配置文件的路径
spring.resources.path=classpath:*.properties
```
### swagger 新增扫描多个包的情况
`swagger2.basePackage` : 用以","号分割
```yaml
swagger2: 
  basePackage: com.elab.marketing.brand.service.impl,com.elab.marketing.brand.controllers
  title: brand
  description: 品牌app微服务
  termsOfServiceUrl: http://www.elab-plus.com
  licenseUrl: http://127.0.0.1:5312/doc.html
  version: 1.0.0
  enable: true
```

### DataSourceConfigBean : 数据源配置

```perl

default.driverClassName=
default.url=
default.username=
default.password=
default.filters=
default.initialSize=5
default.minIdle=10
default.maxActive=50
```


### JdbcBeanConfig : JDBC封装类配置
可选配置
```perl
# jdbc的sql文件目录
jdbc.config.path=sql

# dao接口层扫描
jdbc.config.scan=com.elab.**

```

### TransactionConfigBean : 事务配置
针对事物相关的类进行配置

> 这里需要注意的是一般 TransactionConfigBean、JdbcBeanConfig 依赖 DataSourceConfigBean 所以使用的时候，需要将这三个类都一次性导入


#### SpringMvcConfig : 基本的SpringMVC容器相关的配置

封装了MultipartResolver等相关配置

```perl
# 文件上传大小配置
mvc.multipartResolver.MaxUploadSize=10485760
```

### CommonException : 全局异常定义
直接注入就拥有全局异常功能

- `BusinessException` ： 该异常表示业务中出现的参数不对,以及一些不会记录Log的异常会直接封装成code+msg返回给前端
- 

### LogResponseBodyAdvice : 日志id作为结果集返回
直接注入拥有全局返回日志id的功能


### SwaggerConfigBean : api对象配置

```
#是否开启swagger
swagger.enable=必填
# controller路径
swagger2.basePackage=必填
# api的标题
swagger2.title=必填
# 描述
swagger2.description=必填
# 团队服务的URL
swagger2.termsOfServiceUrl=必填
# 许可证地址
swagger2.licenseUrl=必填
# 版本号
swagger2.version=必填
```


### RestTemplateUtils
- 新增`IRestFallback`回调接口
    - `DefaultRestFallBack` 默认的回调 ： 一旦请求出现异常，则将该次请求数据记录到`mng_http_failure_data`表中
> 如果业务有特殊处理，可以实现该接口去覆盖DefaultRestFallBack类的实现。

```sql
CREATE TABLE `mng_http_failure_data` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `url` varchar(200) DEFAULT NULL COMMENT '请求路径',
  `req_method` varchar(10) DEFAULT NULL COMMENT '请求方式 GET/POST',
  `req_body` text COMMENT '请求参数',
  `res_body` text COMMENT '返回结果',
  `header_body` text COMMENT '请求头信息',
  `req_retry` int(11) DEFAULT NULL COMMENT '请求重试次数',
  `req_status` int(11) DEFAULT NULL COMMENT '请求状态 -1失败 1成功 ',
  `cat_id` varchar(50) DEFAULT NULL COMMENT '链路编号',
  `error_msg` varchar(500) DEFAULT NULL,
  `status` int(11) DEFAULT NULL COMMENT '状态：1  有效  -1  无效',
  `created` datetime DEFAULT NULL COMMENT '创建时间',
  `updated` datetime DEFAULT NULL COMMENT '修改时间',
  `creator` varchar(20) DEFAULT NULL COMMENT '创建者',
  `updator` varchar(20) DEFAULT NULL COMMENT '修改者',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='http故障数据记录';
```
**如何应用到业务中**

1. 配置类
```java
@Bean
public IRestFallback restFallback() {
	DefaultRestFallBack restFallBack = new DefaultRestFallBack();
   return restFallBack;
}

@Bean
public RestTemplateUtils restTemplateUtils(@Autowired RestTemplate restTemplate) {
	RestTemplateUtils restTemplateUtils = new RestTemplateUtils();
	restTemplateUtils.setRestTemplate(restTemplate);
	// 将默认的配置类加入到其中
	restTemplateUtils.setRestFallback(restFallback());
	return restTemplateUtils;
}
```

具体使用:

```java

// 默认采用的失败回调 -> DefaultRestFallBack
String post = restTemplateUtils.post(url, jsonObject,
  String.class);

// 自定义采用的失败回调 --> 实现IRestFallback接口 = MyRestFallBack
String post = restTemplateUtils.post(url, jsonObject,
  String.class, new MyRestFallBack());

```

### 异步线程

里面封装了消息串联的逻辑，当异步线程产生时，会和主线程的消息进行串联。

**将线程加入异步队列**

加入进去的参数会先放入异步队列进行排队，然后通过消费线程消费该方法。

```java
// 希望被记录下来的消息内容
Map<String,String> dataMap = new HashMap();
dataMap.put("id","asdsa");
ThreadProcessUtils.addRealTimeQueue(() -> {
    logger.info("这是一个异步消息" + Cat.getCurrentMessageId());
    // 做业务操作.
}, dataMap);
```

**将加入调度队列**

这里是负责周期性执行数据

```java
ThreadProcessUtils.addSchedulingList(()->{
    System.out.println("这是一个调度操作" + Cat.getCurrentMessageId());
});
```

**配置相关**

```yaml
spring:  
  elab:
    thread:
      real-queue-size: 1000			# 异步队列大小
      scheduling-interval-second: 30 # 调度间隔时长(秒)
```

#### 并行处理

该框架的使用方式和`CompletableFuture`基本保持一致，只是仅仅涵盖了链路串联功能。
希望大家能够统一使用框架封装的，好处就是在修改的时候能统一修改，调优也是。

- 单个请求
```java
CompletableFuture<String> stringCompletableFuture = ThreadProcessUtils.supplyAsync(() -> {
    logger.info("任务开始之前:" + Cat.getCurrentMessageId());
    try {
        Thread.sleep(3000);
    } catch (Exception e) {
    }
    logger.info("任务开始之后:" + Cat.getCurrentMessageId());
    return "ABC";
});

// 在此期间可以执行其他业务操作

// 需要注意的是到这一步是阻塞的。
String s = stringCompletableFuture.get();
```

- 多个请求,按照顺序返回结果
```java
// 这里需要注意的是第一个参数，true代表是等所有结果加载完了才返回，false是先提交任务执行，这个过程不一定能拿到结果。
CompletableFuture<String>[] completableFutures = ThreadProcessUtils.supplyAsync(true,() -> {
    logger.info("任务1 - 开始" + Cat.getCurrentMessageId());
    Thread.sleep(3000);
    logger.info("任务1 - 结束" + Cat.getCurrentMessageId());
    return "ABC1";
}, () -> {
    logger.info("任务2 - 开始:" + Cat.getCurrentMessageId());
    logger.info("任务2 - 结束" + Cat.getCurrentMessageId());
    return "ABC2";
});

// 如果上面是true表示结果加载完了才会执行到这里,如果是false的话,就需要这里阻塞去拿结果.
for (int i = 0; i < completableFutures.length; i++) {
    String s = completableFutures[i].get();
    logger.info("结果:" + s);
}
```

- 多个请求,按照提交的key拿到结果
```java

Map<String, Supplier<String>> taskMap = new LinkedHashMap<>();
taskMap.put("A", () -> {
    logger.info("A 开始" + Cat.getCurrentMessageId());
    sleep(1000);
    logger.info("A 结束" + Cat.getCurrentMessageId());
    return "A-result";
});

taskMap.put("B", () -> {
    logger.info("B 开始" + Cat.getCurrentMessageId());
    sleep(1000);
    logger.info("B 结束" + Cat.getCurrentMessageId());
    return "B-result";
});

taskMap.put("C", () -> {
    logger.info("C 开始" + Cat.getCurrentMessageId());
    sleep(1000);
    logger.info("C 结束" + Cat.getCurrentMessageId());
    return "C-result";
});

Map<String, CompletableFuture<String>> completableFutureMap = ThreadProcessUtils.supplyAsync(taskMap);


// 根据提交的key，获取对应的值
String a = completableFutureMap.get("A").get();
String b = completableFutureMap.get("B").get();
String c = completableFutureMap.get("C").get();

logger.info("获取对应的结果:" + a);
logger.info("获取对应的结果:" + b);
logger.info("获取对应的结果:" + c);
```