405 lines
11 KiB
Markdown
405 lines
11 KiB
Markdown
# Spring模板
|
||
|
||
每个服务下都有`Dockerfile`文件,几乎是写好的模板,如果要添加在这基础上即可。
|
||
|
||
- 基础包有
|
||
- 邮件发送
|
||
- WebSocket
|
||
- Minio
|
||
- Redis
|
||
- rabbitMq
|
||
- velocity
|
||
- IP地址查询
|
||
- knife4j
|
||
- 数据库多源配置
|
||
|
||
## 基础配置
|
||
|
||
### 配置文件详情
|
||
|
||
### 打包命令
|
||
|
||
命令解释:清理之前内容,打包,使用生产环境,跳过测试
|
||
|
||
```shell
|
||
mvn clean package -DskipTests
|
||
mvn clean package -Pprod -DskipTests
|
||
```
|
||
|
||
#### SpringBoot配置文件
|
||
|
||
在开发中需要使用到开发环境、上线需要生产环境,在环境中设置`@profiles.active@`可以根据不同环境切换
|
||
|
||
```yaml
|
||
spring:
|
||
profiles:
|
||
active: @profiles.active@
|
||
application:
|
||
name: service-admin
|
||
```
|
||
|
||
只需要在IDE中勾选相关环境即可
|
||
|
||
![image-20240822093552802](./images/image-20240822093552802.png)
|
||
|
||
> 注意!!!
|
||
>
|
||
> 因为Java每次启动都需要生成target,有缓存在里面,很有可能明明选择了配置但是没有生效的情况。
|
||
>
|
||
> 解决办法就是,每次改变环境执行`mvn clean`或者点击IDE中`mvn clean`
|
||
>
|
||
> ![image-20240822093803078](./images/image-20240822093803078.png)
|
||
|
||
### Dockerfile配置
|
||
|
||
如果需要访问宿主机文件目录,这个是Docker内部地址
|
||
|
||
```dockerfile
|
||
# 程序内部挂在目录
|
||
VOLUME /home/server/uploads
|
||
```
|
||
|
||
#### IDE中配置
|
||
|
||
![image-20240822094021339](./images/image-20240822094021339.png)
|
||
|
||
![image-20240822094000542](./images/image-20240822094000542.png)
|
||
|
||
### 整体返回响应
|
||
|
||
整体返回响应如下
|
||
|
||
```java
|
||
// 状态码
|
||
private Integer code;
|
||
// 返回消息
|
||
private String message;
|
||
// 返回数据
|
||
private T data;
|
||
```
|
||
|
||
![image-20240822092441020](./images/image-20240822092441020.png)
|
||
|
||
和分页返回
|
||
|
||
```java
|
||
/**
|
||
* 封装分页查询结果
|
||
*/
|
||
@Data
|
||
@AllArgsConstructor
|
||
@NoArgsConstructor
|
||
@Builder
|
||
public class ResultPage<T> implements Serializable {
|
||
// 当前页
|
||
private Integer pageNo;
|
||
// 每页记录数
|
||
private Integer pageSize;
|
||
// 总记录数
|
||
private long total;
|
||
// 当前页数据集合
|
||
private List<T> list;
|
||
}
|
||
```
|
||
|
||
以及常用的枚举状态码(展示部分)
|
||
|
||
![image-20240822092510151](./images/image-20240822092510151.png)
|
||
|
||
### 多数据库源配置
|
||
|
||
开发中有时会使用到多个数据库源,这个配置也是来自MybatisPlus官方推荐的库
|
||
|
||
```xml
|
||
<!-- 多数据库源插件 -->
|
||
<dependency>
|
||
<groupId>com.baomidou</groupId>
|
||
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
|
||
<version>4.3.1</version>
|
||
</dependency>
|
||
```
|
||
|
||
#### 配置简介
|
||
|
||
如果不需要多数据库,移除包之后将注释的部分放开,删除`dynamic`节点以下内容
|
||
|
||
```
|
||
datasource:
|
||
#type:com.zaxxer.hikari.HikariDataSource
|
||
#driver-class-name:com.mysql.cj.jdbc.Driver
|
||
#url:jdbc:mysql://${bunny.datasource.host}:${bunny.datasource.port}/${bunny.datasource.sqlData}?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true
|
||
#username:
|
||
|
||
$ {
|
||
bunny.datasource.username
|
||
}
|
||
#password:
|
||
|
||
$ {
|
||
bunny.datasource.password
|
||
}
|
||
|
||
dynamic:
|
||
primary:master #设置默认的数据源或者数据源组,默认值即为master
|
||
strict:false #严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源
|
||
grace-destroy:false #是否优雅关闭数据源,默认为false,设置为true时,关闭数据源时如果数据源中还存在活跃连接,至多等待10s后强制关闭
|
||
datasource:
|
||
master:
|
||
type:com.zaxxer.hikari.HikariDataSource
|
||
driver-class-name:com.mysql.cj.jdbc.Driver
|
||
url:jdbc:mysql://${bunny.datasource.host}:${bunny.datasource.port}/${bunny.datasource.sqlData}?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true
|
||
username:
|
||
|
||
$ {
|
||
bunny.datasource.username
|
||
}
|
||
|
||
password:
|
||
|
||
$ {
|
||
bunny.datasource.password
|
||
}
|
||
|
||
aop:
|
||
enabled:true
|
||
```
|
||
|
||
## 中间件配置
|
||
|
||
### mybatis-plus
|
||
|
||
#### 配置详情
|
||
|
||
配置乐观锁、防止全表删除、最大分页100页
|
||
|
||
`common/service-utils/src/main/java/cn/bunny/common/service/config/MybatisPlusConfig.java`
|
||
|
||
```java
|
||
/**
|
||
* Mybatis-Plus配置类
|
||
*/
|
||
@EnableTransactionManagement
|
||
@Configuration
|
||
@Slf4j
|
||
public class MybatisPlusConfig {
|
||
@Bean
|
||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||
// 分页插件
|
||
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
|
||
paginationInnerInterceptor.setMaxLimit(100L);// 设置最大分页为100
|
||
interceptor.addInnerInterceptor(paginationInnerInterceptor);
|
||
// 乐观锁
|
||
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
|
||
// 防止全表删除
|
||
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
|
||
|
||
return interceptor;
|
||
}
|
||
}
|
||
```
|
||
|
||
如果如要插入和修改时,自定义时间或者其它可以在这设置
|
||
|
||
`common/service-utils/src/main/java/cn/bunny/common/service/config/MyBatisPlusFieldConfig.java`
|
||
|
||
```java
|
||
/**
|
||
* 配置MP在修改和新增时的操作
|
||
*/
|
||
@Component
|
||
public class MyBatisPlusFieldConfig implements MetaObjectHandler {
|
||
|
||
/**
|
||
* 使用mp做添加操作时候,这个方法执行
|
||
*/
|
||
@Override
|
||
public void insertFill(MetaObject metaObject) {
|
||
// 设置属性值
|
||
this.setFieldValByName("createTime", new Date(), metaObject);
|
||
this.setFieldValByName("updateTime", new Date(), metaObject);
|
||
this.setFieldValByName("deleteStatus", 1, metaObject);
|
||
if (BaseContext.getUsername() != null) {
|
||
this.setFieldValByName("createBy", BaseContext.getUsername(), metaObject);
|
||
this.setFieldValByName("updateBy", BaseContext.getUsername(), metaObject);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 使用mp做修改操作时候,这个方法执行
|
||
*/
|
||
@Override
|
||
public void updateFill(MetaObject metaObject) {
|
||
this.setFieldValByName("updateTime", new Date(), metaObject);
|
||
this.setFieldValByName("updateBy", BaseContext.getUsername(), metaObject);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Redis
|
||
|
||
#### 配置详情
|
||
|
||
分别设置了过期30天、1小时、3分钟
|
||
|
||
`common/service-utils/src/main/java/cn/bunny/common/service/config/RedisConfiguration.java`
|
||
|
||
```java
|
||
/**
|
||
* * 配置Redis过期时间30天
|
||
* 解决cache(@Cacheable)把数据缓存到redis中的value是乱码问题
|
||
*/
|
||
@Bean
|
||
@Primary
|
||
@SuppressWarnings("all")
|
||
public CacheManager cacheManagerWithMouth(RedisConnectionFactory factory) {
|
||
// 配置序列化
|
||
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
|
||
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
|
||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer()))
|
||
.entryTtl(Duration.ofDays(30));
|
||
|
||
return RedisCacheManager.builder(factory).cacheDefaults(config).build();
|
||
}
|
||
|
||
/**
|
||
* * 配置redis过期时间3分钟
|
||
*
|
||
* @param factory
|
||
* @return
|
||
*/
|
||
@Bean
|
||
@SuppressWarnings("all")
|
||
public CacheManager cacheManagerWithMinutes(RedisConnectionFactory factory) {
|
||
// 配置序列化
|
||
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
|
||
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
|
||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer()))
|
||
.entryTtl(Duration.ofMinutes(3));
|
||
|
||
return RedisCacheManager.builder(factory).cacheDefaults(config).build();
|
||
}
|
||
|
||
/**
|
||
* * 配置Redis过期时间1小时
|
||
*
|
||
* @param factory
|
||
* @return
|
||
*/
|
||
@Bean
|
||
@SuppressWarnings("all")
|
||
public CacheManager cacheManagerWithHours(RedisConnectionFactory factory) {
|
||
// 配置序列化
|
||
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
|
||
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
|
||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer()))
|
||
.entryTtl(Duration.ofHours(1));
|
||
|
||
return RedisCacheManager.builder(factory).cacheDefaults(config).build();
|
||
}
|
||
```
|
||
|
||
#### 使用详情
|
||
|
||
如果需要指定Redis配置,`cacheManager="Redis配置中方法名"`
|
||
|
||
使用springCache只需要在方法上加上下面代码
|
||
|
||
```java
|
||
@Cacheable(value = "TaskStatistics", key = "'ByDepartment::'+#departmentName",
|
||
cacheManager = "cacheManagerWithMinutes")
|
||
```
|
||
|
||
### Minio
|
||
|
||
#### 配置详情
|
||
|
||
Minio没有给出SpringBoot的配置文件,下面是自定义实现
|
||
|
||
`module/module-minio/src/main/java/cn/bunny/module/minio/properties/MinioProperties.java`
|
||
|
||
在配置文件中有这4个配置字段
|
||
|
||
```java
|
||
|
||
@Configuration
|
||
@ConfigurationProperties(prefix = "bunny.minio")
|
||
@ConditionalOnProperty(name = "bunny.minio.bucket-name")// 当属性有值时这个配置才生效
|
||
@Data
|
||
@Slf4j
|
||
public class MinioProperties {
|
||
private String endpointUrl;
|
||
private String accessKey;
|
||
private String secretKey;
|
||
private String bucketName;
|
||
|
||
@Bean
|
||
public MinioClient minioClient() {
|
||
log.info("注册MinioClient...");
|
||
return MinioClient.builder().endpoint(endpointUrl).credentials(accessKey, secretKey).build();
|
||
}
|
||
}
|
||
```
|
||
|
||
在项目中加入了Minio常用工具方法,对Minio二次封装
|
||
|
||
![image-20240822091720866](./images/image-20240822091720866.png)
|
||
|
||
### 邮箱发送
|
||
|
||
邮箱发送配置的是动态邮件,发件人是动态的不是写死在配置文件中
|
||
|
||
![image-20240822091810597](./images/image-20240822091810597.png)
|
||
|
||
#### 配置文件
|
||
|
||
如果不需要动态配置可以在`SpringBoot`配置文件中加入下面的配置
|
||
|
||
```properties
|
||
mail=
|
||
host=smtp.qq.com # 邮箱地址
|
||
port=465 # 邮箱端口号
|
||
username=xxx@qq.com # 设置发送邮箱
|
||
password=xx # 如果是纯数字要加引号
|
||
default-encoding=UTF-8 # 设置编码格式
|
||
protocol=smtps
|
||
properties=
|
||
mail=
|
||
debug=true # 是否开启debug模式发送邮件
|
||
smtp=
|
||
auth=true
|
||
connectionTimeout=5000 # 设置连接延迟
|
||
timeout=5000 # 延迟时间
|
||
writeTimeout=5000 # 写入邮箱延迟
|
||
allow8BitMime=true
|
||
sendPartial=true
|
||
ssl=
|
||
enabled=true # 是否开启SSL连接
|
||
socketFactory=
|
||
class=javax.net.ssl.SSLSocketFactory # 必要设置!!!
|
||
```
|
||
|
||
### SpringSecurity
|
||
|
||
因为项目做的是开发模板,在admin模板中集成了安全框架
|
||
|
||
`module/spring-security/src/main/java/cn/bunny/security/config/WebSecurityConfig.java`
|
||
|
||
在这个文件最下面是排除路径,不需要Security检测的路径,根据自己需求进行修改,因为整合了knife4j在测试时,需要放开swagger配置响应请求等。
|
||
|
||
```java
|
||
/**
|
||
* * 排出鉴定路径
|
||
*
|
||
* @return WebSecurityCustomizer
|
||
*/
|
||
@Bean
|
||
public WebSecurityCustomizer webSecurityCustomizer() {
|
||
String[] annotations = {"/", "/test/**", "/diagram-viewer/**", "/editor-app/**", "/*.html",
|
||
"/*/*/noAuth/**", "/*/noAuth/**", "/favicon.ico", "/swagger-resources/**", "/webjars/**",
|
||
"/v3/**", "/swagger-ui.html/**", "/doc.html"};
|
||
return web -> web.ignoring().requestMatchers(annotations);
|
||
}
|
||
```
|
||
|