# lh-schedule-spring
**Repository Path**: qingchengfeiyu/lh-schedule-v1
## Basic Information
- **Project Name**: lh-schedule-spring
- **Description**: 日复一日,学习总结
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2021-11-18
- **Last Updated**: 2022-10-25
## Categories & Tags
**Categories**: Uncategorized
**Tags**: 架构
## README
# 学习日程

## 自定义配置详解 (更新)
> 一下配置只用在yml中配置即可,在基础模块中可以查看相关代码,其他模块只需要引入基础模块pom,并且在启动类中加入@SpringBootApplication(scanBasePackages = {"com.schedule"})
>
> 即可完成配置
### 1.缓存字典功能 (维护中)
*基础模块中配置了全自动字典翻译功能,如需自行配置实现DictCacheService类完成提供字典源*
*备用处理实现AlternateProcessingService类中gatNameAndRefactoringMap方法,更新源信息,并返回处理*
```yaml
lh-config:
# 配置缓存字典翻译
automatic:
translation:
redis-cache: true
```
**默认false表示存储内存中,配置为true需要连接redis,表示字典项将存储在缓存之中**
### 2.线程池配置 (维护中)
```yaml
lh-config:
task-pool:
corePoolSize: 2 #核心线程池大小
maxPoolSize: 3 # 最大线程数
keepAliveSeconds: 300 # 活跃时间
queueCapacity: 4 #队列容量
prefixName: customize-thread- #前缀名
```
### 3.全自动数据库维护
```yaml
lh-config:
flyway-config:
switch: true #自定义注解,配置开启关闭全自动数据库版本管理
locations: classpath:db/migration
baseline-on-migrate: true
validate-on-migrate: true
table: sys_flyway_history # 自定义数据表名
```
### 4.swagger整合log全自动日志
> 默认扫描类中api注解,存在时自动开启切面日志
>
> @Api("商品模块") 为一级路径介绍
>
> @ApiOperation("添加商品") 为二级路径介绍
>
> @ApiOperation(value = "查询商品及其附加分类信息",notes = "SELECT") 不写notes默认操作类型为未知
```yaml
lh-config:
swagger-config: #swagger扫包路径
base-package: com.schedule.zyjshopping
```
## 1.SpringBoot自定义注解
> 常用注解 @interface
#### **@Target:修饰对象作用范围**
| 值(ElementType) | 作用 | 备注 |
| --------------- | ------------------------------------- | ---- |
| CONSTRUCTOR | 描述构造器 | |
| FIELD | 描述域 | |
| LOCAL_VARIABLE | 描述局部变量 | |
| METHOD | 描述方法 | 常用 |
| PACKAGE | 描述包 | |
| PARAMETER | 描述参数 | |
| TYPE | 描述类、接口(包括注解类型) 或enum声明 | 常用 |
#### **@Retention**:修饰生命周期
| 值(ElementType) | 作用 | 备注 |
| --------------- | -------------------------------- | ---- |
| SOURCE | 在源文件中有效(即源文件保留) | |
| CLASS | 在class文件中有效(即class保留) | |
| RUNTIME | 在运行时有效(即运行时保留) | 常用 |
#### **@**Documented:注解表明这个注解应该被 javadoc工具记录
#### **@Inherited** :允许子类继承

## 2.SpringBoot切面AOP
> 什么是aop?
AOP(Aspect Oriented Programming)意为“面向切面编程”。面向切面编程的目标就是分离关注点,不改变源代码情况下,增强逻辑,适用于日志,权限验证,加密解密等处理.....
官方介绍:在软件业,AOP为Aspect Oriented Programming的缩写,意为:**面向切面编程**,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
简单来说:就是切面编程,通过动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
主要功能:打印日志,性能统计,安全控制,事务处理,异常处理等等。
### 2.1 AOP依赖
```xml
org.springframework.boot
spring-boot-starter-aop
```
### 2.2 实现AOP切面
只需要加上必备的@Aspect 和@Component 注解
@Component :将该类交给 Spring 来管理
@Aspect :描述一个切面类
```java
@Aspect
@Component
@Slf4j
public class DSAspect {
}
```
### 2.3 常用注解
| 注解名 | 含义 | 备注 |
| --------------- | ------------------------------------------------------------ | ---- |
| @Aspect | 声明该类为一个注解类 | |
| @Pointcut | 定义一个切点,后面跟随一个表达式,表达式可以定义为某个 package 下的方法,也可以是自定义注解等 | |
| @Before | 在切点之前,织入相关代码 | |
| @After | 在切点之后,织入相关代码 | |
| @AfterReturning | 在切点返回内容后,织入相关代码,一般用于对返回值做些加工处理的场景 | |
| @AfterThrowing | 用来处理当织入的代码抛出异常后的逻辑处理 | |
| @Around | 在切入点前后织入代码,并且可以自由的控制何时执行切点 | |
### 2.4 切点函数
| 函数名 | 作用 | 备注 |
| ---------- | ---------------------- | ------------ |
| execution | 指明包下方法来定义切面 | 常用 重点 |
| annotation | 指明注解来定义切面 | 常用 重点 |
| within | 指明java类或者包 | |
| this | 用于传入代理对象 | |
| target | 传入对象引用 | |
| args | 传入参数 | 常用 |

## 3.全自动字典翻译
需要**实现DictCacheService接口**,并将这个对象交给Spring 容器,否则扩展将不会自动生效
举例:
```java
@Component
public class DictCache implements DictCacheService {
@Override
public BiMap getDictMapByName(String dictName) {
//举例,通过字典名称返回所有的字典项
BiMap map = HashBiMap.create();
switch (dictName) {
case "sex":
map.put("1", "男");
map.put("2", "女");
break;
case "car:color":
map.put("1", "芭比粉");
map.put("2", "烈焰红");
break;
case "license:type":
map.put("A1","大型汽车");
map.put("C1","小型汽车");
map.put("C2","小型自动挡汽车");
map.put("D","普通三轮摩托车");
break;
}
return map;
}
}
```
### 实体类注解说明
#### @Dict
> 标记一个字段进行字典翻译
| 注解名 | 含义 | 描述 | 默认值 |
| -------------- | -------------- | ------------------------------------------------------------ | ------------- |
| dictName | 翻译字典名称 | 明翻译这个字段使用的字典名称,与数据sys_dict.name对应 | |
| targetField | 目标字段 | 用于存放字典翻译之后的name的值 | 前字段名+Name |
| nullValueName | 空值默认值 | 当注解的字段为空时的默认值,与表格显示所对应 | - |
| undefinedValue | 空字典项默认值 | 当翻译的字段在对应的字典中未定义时的默认值 | “” |
| multiple | 是否多个字典项 | 标记这个字段是否包含多个字典项,为true时将会按照","进行拆分,然后逐个翻译之后放到目标字段中 | false |
#### @DictEntity
> 标记说明类中的这个字段是一个实体类型,其中有字段进行字典翻译。
#### @DictCollection
> 标记说明类中的这个字段是一个Collection类型,其中有字段进行字典翻译。
#### @DictTranslation
> 使用这个注解的方法将进行字典翻译,其返回值类型需要是一个含有@Dcit、@DictCollection或@DictTranslation的类型或者集合类型
#### @DictMapper
> 这个注解相比@Dict多了一个参数“fieldName”,给出翻译的源key。
#### @DictMap
> 用于标记一个返回值是java.util.Map子类的方法。其中参数为@DictMapper数组
### 设计思路
> 基于Spring Boot使用aop对标记注解的方法进行拦截 >> 利用反射获取到翻译信息 >> 反射获取值翻译并将值填写回对象。
> 封装成Starter,为了能自动装配到不同的项目,定义接口DictCacheService,是否含有这个接口的实例化对象是切面是否生效的关键
> DictCacheService.getDictMapByName为了从项目的字典目录中根据字典名称获取到字典项,其中返回值使用双向链表BiMap是为了后期扩展根据name查找对应的value(例如在excel导入的时候可以逆向翻译),这些也都是我的一些想法,还不知道能少时候做出来,哈哈
> DictCacheService 名字的来源,为了在翻译的时候不用每次都从数据库里取值,所以叫缓存

## 4.整合redis缓存处理
> 加入依赖
```xml
org.springframework.boot
spring-boot-starter-redis
1.4.7.RELEASE
redis.clients
jedis
```
> 配置文件
```yaml
spring:
# redis 配置
redis:
# 地址
host: localhost
# 端口,默认为6379
port: 6379
# 密码
password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
```
### **创建RedlisConfig**
用于Redis数据缓存。继承CachingConfigurerSupport,为了自定义生成KEY的策略。可以不继承
```java
/**
* redis配置类
**/
@Configuration
@EnableCaching//开启注解式缓存
//继承CachingConfigurerSupport,为了自定义生成KEY的策略。可以不继承。
public class RedisConfig extends CachingConfigurerSupport {
/**
* 生成key的策略 根据类名+方法名+所有参数的值生成唯一的一个key
*
* @return
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
/**
* 管理缓存
*
* @param redisConnectionFactory
* @return
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
//通过Spring提供的RedisCacheConfiguration类,构造一个自己的redis配置类,从该配置类中可以设置一些初始化的缓存命名空间
// 及对应的默认过期时间等属性,再利用RedisCacheManager中的builder.build()的方式生成cacheManager:
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); // 生成一个默认配置,通过config对象即可对缓存进行自定义配置
config = config.entryTtl(Duration.ofMinutes(1)) // 设置缓存的默认过期时间,也是使用Duration设置
.disableCachingNullValues(); // 不缓存空值
// 设置一个初始化的缓存空间set集合
Set cacheNames = new HashSet<>();
cacheNames.add("my-redis-cache1");
cacheNames.add("my-redis-cache2");
// 对每个缓存空间应用不同的配置
Map configMap = new HashMap<>();
configMap.put("my-redis-cache1", config);
configMap.put("my-redis-cache2", config.entryTtl(Duration.ofSeconds(120)));
RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory) // 使用自定义的缓存配置初始化一个cacheManager
.initialCacheNames(cacheNames) // 注意这两句的调用顺序,一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
.withInitialCacheConfigurations(configMap)
.build();
return cacheManager;
}
@Bean
public RedisTemplate