背景
本文是《Java 后端从小白到大神》修仙系列之框架学习,Java框架之 SpringBoot
框架第四篇
。SpringBoot框架
可以说是微服务的基石,很多复杂的系统几乎都是通过微服务构造而来,若想详细学习请点击首篇博文开始,现在开始学习。
文章概览
- Spring Boot 集成 Spring Security 实现登录认证
- Spring Boot 项目持续集成(Jenkins + Git)
- 链路追踪(Sleuth + Zipkin)实现服务调用监控
- 开发一个自定义 Spring Boot Starter
- Spring Boot 总结与进阶指引
1. Spring Boot 集成 Spring Security 实现登录认证
一、引入依赖
1
2
3
4
|
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
|
二、定义配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
|
三、自定义用户信息
1
2
3
4
5
|
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password("{noop}123456").roles("ADMIN");
}
|
2. Spring Boot 项目持续集成(Jenkins + Git)
一、Jenkins Job 基本配置:
- 源码地址配置 GitHub 仓库
- 构建触发器设置为 Git push 或定时拉取
二、构建步骤:
1
2
3
4
|
#!/bin/bash
mvn clean package -DskipTests
docker build -t myapp:latest .
docker-compose down && docker-compose up -d
|
3. 链路追踪(Sleuth + Zipkin)实现服务调用监控
一、引入依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
老版本:spring boot 2.x
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
新版本:spring boot 3.x
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>
|
二、配置 Zipkin 地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
老版本:spring boot 2.x
spring:
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1.0
新版本:spring boot 3.x
spring:
zipkin:
base-url: http://zipkin:9411
enabled: true
management:
tracing:
sampling:
probability: 1.0 # 全采样,开发阶段建议设为 1.0
propagation:
type: b3 # 或者使用 w3c,默认也是 b3,保持兼容
|
4. 开发一个自定义 Spring Boot Starter
一、创建模块并定义 AutoConfiguration 类
1
2
3
4
5
6
7
8
|
@Configuration
@ConditionalOnClass(MyService.class)
public class MyServiceAutoConfiguration {
@Bean
public MyService myService() {
return new MyService();
}
}
|
二、添加 spring.factories
1
2
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.example.MyServiceAutoConfiguration
|
5. Spring Boot 总结与进阶指引
一、核心知识体系回顾
- 自动配置机制(
@EnableAutoConfiguration
)
- 配置文件加载机制(
application.yml/properties
)
- 多环境支持(
@Profile
)
- 配置元数据提示(
spring-configuration-metadata.json
)
二、开发经验要点
- 日志与配置分类管理
- 合理使用自动配置和手动配置的边界
- 项目结构模块化拆分建议
三、为学习 Spring Cloud 做准备
- 服务注册与发现(Eureka / Nacos)
- 配置中心 / 网关 / 熔断降级 / 服务间通信(Feign)
6. 综合上述内容练习
项目名称:CloudTask 平台,这是个小项目,纯纯练手的,但是五脏俱全,一个基于 Spring Cloud 构建的定时任务调度与执行平台,支持用户登录、任务注册、定时触发、执行状态跟踪、调用链监控、统一配置管理、消息通知等功能。所有服务都部署在 docker 上面,通过 jenkins 自动构建和部署。
项目架构图(微服务拆分)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
+-------------+
| Zipkin |
+------+------+
|
+-----------▼-----------+
| Gateway 网关服务 |
+-----------+-----------+
|
+-------------------------+-------------------------+
| | |
+--------▼--------+ +--------▼--------+ +--------▼--------+
| auth-service | | task-service | | user-service |
| 登录认证服务 | | 任务调度执行服务 | | 用户信息管理服务 |
+--------+--------+ +--------+--------+ +--------+--------+
| | |
+-------▼--------+ +--------▼--------+ +--------▼--------+
| Spring Security | | Redis缓存任务 | | MySQL + JPA ORM |
+-----------------+ +-----------------+ +-----------------+
|
项目总目录结构:cloud-task
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
cloud-task/
├── docker/ # 所有 Docker 容器配置(包括 redis、rabbitmq、nacos)
│── └── docker-compose.yml # Docker 容器启动配置
│── └── jdbc.properties # Ncaos 数据库连接信息
├── gateway-service/ # 网关服务(Spring Cloud Gateway)
│── └── Dockerfile # Dockerfile 文件
├── auth-service/ # 登录服务(JWT 登录 + Redis 缓存)
│── └── Dockerfile # Dockerfile 文件
├── user-service/ # 用户服务(注册、查询等)
│── └── Dockerfile # Dockerfile 文件
├── task-service/ # 定时任务调度与消费服务(含 @Scheduled 与 MQ 消费)
│── └── Dockerfile # Dockerfile 文件
├── starter-task-common/ # 自定义 Starter,用于任务日志 AOP 记录
├── common/ # 公共模块(DTO、返回结果、全局异常、工具类等)
├── config/ # Nacos 配置文件(集中管理服务配置)
│── └── nacos-config.yaml # Nacos 配置文件
├── └── auth-service-dev.yaml # 认证服务开发环境配置
├── └── auth-service-prod.yaml # 认证服务生产环境配置
├── └── gateway-service-dev.yaml # 网关服务开发环境配置
├── └── gateway-service-prod.yaml # 网关服务生产环境配置
├── └── task-service-dev.yaml # 任务服务开发环境配置
├── └── task-service-prod.yaml # 任务服务生产环境配置
├── └── user-service-dev.yaml # 用户服务开发环境配置
├── └── user-service-prod.yaml # 用户服务生产环境配置
├── └── shared-common.yaml # 服务通用配置
├── Jenkinsfile # 自动化构建脚本
└── README.md
|
项目运行环境:nacos、redis、mysql、rabbitmq、zipkin、jenkins、auth-service、user-service、task-service、gateway-service 这些服务都部署在docker容器中。代码提交 https://gitee.com/null_493_9051/cloudtask.git 后,通过 jenkin 自动部署服务。
项目代码:
auth service 模块代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package com.yutao.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
// 表示这是一个配置类,会被 Spring 容器识别和加载
@Configuration
// 启用 Spring Security 的 Web 安全支持
@EnableWebSecurity
public class SecurityConfig {
// 定义一个 SecurityFilterChain Bean,用来配置 Spring Security 的过滤器链
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// 关闭 CSRF(跨站请求伪造)保护,通常对前后端分离的 REST 接口不需要开启
http.csrf(csrf -> csrf.disable())
// 设置所有请求不进行认证,全部放行
.authorizeRequests(requests -> requests.anyRequest().permitAll());
// 返回构建好的安全过滤器链
return http.build();
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
package com.yutao.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.yutao.dto.ApiResponse;
import com.yutao.dto.LoginDTO;
import com.yutao.dto.RegisterDTO;
import com.yutao.service.AuthService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequestMapping("")
public class AuthController {
@Autowired private AuthService authService;
@PostMapping("/login")
public ApiResponse<?> login(@RequestBody LoginDTO dto) {
log.info("login接口请求参数: {}", dto.getUsername());
String token = authService.login(dto);
return ApiResponse.success(token);
}
@PostMapping("/register")
public ApiResponse<?> register(@RequestBody RegisterDTO dto) {
log.info("register接口请求参数: {}", dto.getUsername());
String token = authService.register(dto);
return ApiResponse.success(token); // 返回 token
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
package com.yutao.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.yutao.dto.ApiResponse;
import com.yutao.dto.LoginDTO;
import com.yutao.dto.RegisterDTO;
import com.yutao.service.AuthService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequestMapping("")
public class AuthController {
@Autowired private AuthService authService;
@PostMapping("/login")
public ApiResponse<?> login(@RequestBody LoginDTO dto) {
log.info("login接口请求参数: {}", dto.getUsername());
String token = authService.login(dto);
return ApiResponse.success(token);
}
@PostMapping("/register")
public ApiResponse<?> register(@RequestBody RegisterDTO dto) {
log.info("register接口请求参数: {}", dto.getUsername());
String token = authService.register(dto);
return ApiResponse.success(token); // 返回 token
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package com.yutao;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.yutao.feign") // 指定Feign接口所在包
public class AuthApplication {
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
application.yml
server:
port: 8001
spring:
application:
name: auth-service
cloud:
nacos:
discovery:
server-addr: nacos:8848
zipkin:
base-url: http://zipkin:9411
enabled: true
management:
tracing:
sampling:
probability: 1.0 # 全采样,开发阶段建议设为 1.0
propagation:
type: b3 # 或者使用 w3c,默认也是 b3,保持兼容
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loadbalancer:
enabled: true
logging:
level:
'[com.yutao.feign]': DEBUG
feign: DEBUG
jwt:
secret: my_super_secret_key_that_is_at_least_32_chars_long!
expiration: 3600000
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
bootstrap.yml 文件
spring:
application:
name: auth-service
cloud:
nacos:
config:
server-addr: nacos:8848
namespace: public
group: DEFAULT_GROUP
file-extension: yaml
discovery:
server-addr: nacos:8848
namespace: public
profiles:
active: dev
|
1
2
3
4
5
|
FROM eclipse-temurin:21-jdk-alpine
WORKDIR /app
COPY target/auth-service-*.jar app.jar
EXPOSE 9000
ENTRYPOINT ["java", "-jar", "app.jar"]
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
pom 文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.yutao</groupId>
<artifactId>cloudtask</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.yutao</groupId>
<artifactId>auth-service</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.yutao</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>
<dependency>
<groupId>com.yutao</groupId>
<artifactId>user-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
|
common 模块代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package com.yutao.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ApiResponse<T> {
private int code;
private String message;
private T data;
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(200, "OK", data);
}
public static ApiResponse<?> error(String msg) {
return new ApiResponse<>(500, msg, null);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package com.yutao.exception;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.yutao.dto.ApiResponse;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ServiceException.class)
public ApiResponse<?> handle(ServiceException ex) {
return ApiResponse.error(ex.getMessage());
}
}
|
1
2
3
4
5
6
7
|
package com.yutao.exception;
public class ServiceException extends RuntimeException {
public ServiceException(String message) {
super(message);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
package com.yutao.utils;
import java.security.Key;
import java.util.Date;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
@RefreshScope
@Component
public class JwtUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration:3600000}")
private long expiration;
// 不再使用 @PostConstruct 初始化 Key,改为每次使用动态生成
private Key getSigningKey() {
return Keys.hmacShaKeyFor(secret.getBytes());
}
public String generateToken(String username) {
Date now = new Date();
Date expireAt = new Date(now.getTime() + expiration);
return Jwts.builder()
.setSubject(username)
.setIssuedAt(now)
.setExpiration(expireAt)
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
public String getUsernameFromToken(String token) throws JwtException {
return Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody()
.getSubject();
}
public boolean isTokenExpired(String token) {
try {
Date expiration = Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody()
.getExpiration();
return expiration.before(new Date());
} catch (JwtException e) {
return true;
}
}
}
|
config 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
auth-servcice-dev.yml # 配置文件
server:
port: 9000
spring:
datasource:
url: jdbc:mysql://mysql:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
application:
name: auth-service
profiles:
active: dev
cloud:
nacos:
config:
server-addr: nacos:8848
namespace: public
group: DEFAULT_GROUP
refresh-enabled: true
file-extension: yaml
shared-configs:
- data-id: shared-common.yaml
group: DEFAULT_GROUP
refresh: true
discovery:
server-addr: nacos:8848
namespace: public
redis:
host: redis
port: 6379
rabbitmq:
host: rabbitmq
port: 5672
username: guest
password: guest
zipkin:
base-url: http://zipkin:9411
enabled: true
custom:
jwt:
secret: my_super_secret_key_that_is_at_least_32_chars_long!
expiration: 3600000
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
auth-servcice-pro.yml # 配置文件
server:
port: 9001
spring:
datasource:
url: jdbc:mysql://mysql:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
application:
name: auth-service
profiles:
active: pro
cloud:
nacos:
config:
server-addr: nacos:8848
namespace: public
group: DEFAULT_GROUP
refresh-enabled: true
file-extension: yaml
shared-configs:
- data-id: shared-common.yaml
group: DEFAULT_GROUP
refresh: true
discovery:
server-addr: nacos:8848
namespace: public
redis:
host: redis
port: 6379
rabbitmq:
host: rabbitmq
port: 5672
username: guest
password: guest
zipkin:
base-url: http://zipkin:9411
enabled: true
custom:
jwt:
secret: my_super_secret_key_that_is_at_least_32_chars_long!
expiration: 3600000
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
gateway-service-dev.yaml # 配置文件
server:
port: 8888
spring:
application:
name: gateway-service
profiles:
active: dev
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
- id: auth
uri: lb://auth-service
predicates:
- Path=/auth/**
filters:
- StripPrefix=1
- id: user
uri: lb://user-service
predicates:
- Path=/users/**
filters:
- StripPrefix=1
- id: task
uri: lb://task-service
predicates:
- Path=/tasks/**
filters:
- StripPrefix=1
nacos:
config:
server-addr: nacos:8848
namespace: public
group: DEFAULT_GROUP
file-extension: yaml
refresh-enabled: true
shared-configs:
- data-id: shared-common.yaml
group: DEFAULT_GROUP
refresh: true
discovery:
server-addr: nacos:8848
namespace: public
zipkin:
base-url: http://zipkin:9411
enabled: true
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
gateway-service-pro.yaml # 配置文件
server:
port: 8080
spring:
application:
name: gateway-service
profiles:
active: pro
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
- id: auth
uri: lb://auth-service
predicates:
- Path=/auth/**
filters:
- StripPrefix=1
- id: user
uri: lb://user-service
predicates:
- Path=/users/**
filters:
- StripPrefix=1
- id: task
uri: lb://task-service
predicates:
- Path=/tasks/**
filters:
- StripPrefix=1
nacos:
config:
server-addr: nacos:8848
namespace: public
group: DEFAULT_GROUP
refresh-enabled: true
file-extension: yaml
shared-configs:
- data-id: shared-common.yaml
group: DEFAULT_GROUP
refresh: true
discovery:
server-addr: nacos:8848
namespace: public
zipkin:
base-url: http://zipkin:9411
enabled: true
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
nacos-config.yaml # 配置文件
spring:
application:
name: ${spring.application.name}
profiles:
active: dev
datasource:
url: jdbc:mysql://mysql:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
redis:
host: redis
port: 6379
password:
database: 0
rabbitmq:
host: rabbitmq
port: 5672
username: guest
password: guest
zipkin:
base-url: http://zipkin:9411
enabled: true
cloud:
nacos:
discovery:
server-addr: nacos:8848
namespace: public
config:
server-addr: nacos:8848
file-extension: yaml
group: DEFAULT_GROUP
refresh-enabled: true
namespace: public
shared-configs:
- data-id: shared-common.yaml
group: DEFAULT_GROUP
refresh: true
management:
endpoints:
web:
exposure:
include: '*'
# 自定义配置项,可在服务中通过 @Value 或 @ConfigurationProperties 注入使用
custom:
jwt:
secret: my_super_secret_key_that_is_at_least_32_chars_long!
expiration: 3600000
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
shared-common.yaml # 配置文件
spring:
application:
name: ${spring.application.name}
profiles:
active: dev
datasource:
url: jdbc:mysql://mysql:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
redis:
host: redis
port: 6379
password:
database: 0
rabbitmq:
host: rabbitmq
port: 5672
username: guest
password: guest
zipkin:
base-url: http://zipkin:9411
enabled: true
cloud:
nacos:
discovery:
server-addr: nacos:8848
namespace: public
config:
server-addr: nacos:8848
file-extension: yaml
group: DEFAULT_GROUP
namespace: public
shared-configs:
- data-id: shared-common.yaml
group: DEFAULT_GROUP
refresh: true
management:
endpoints:
web:
exposure:
include: '*'
# 自定义配置项,可在服务中通过 @Value 或 @ConfigurationProperties 注入使用
custom:
jwt:
secret: my_super_secret_key_that_is_at_least_32_chars_long!
expiration: 3600000
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
task-service-dev.yaml # 配置文件
server:
port: 9004
spring:
application:
name: task-service
profiles:
active: dev
zipkin:
base-url: http://zipkin:9411
enabled: true
cloud:
nacos:
config:
server-addr: nacos:8848
namespace: public
group: DEFAULT_GROUP
refresh-enabled: true
file-extension: yaml
shared-configs:
- data-id: shared-common.yaml
group: DEFAULT_GROUP
refresh: true
discovery:
server-addr: nacos:8848
namespace: public
rabbitmq:
host: rabbitmq
port: 5672
username: guest
password: guest
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
task-service-pro.yaml #配置文件
server:
port: 9005
spring:
application:
name: task-service
profiles:
active: pro
zipkin:
base-url: http://zipkin:9411
enabled: true
cloud:
nacos:
config:
server-addr: nacos:8848
namespace: public
group: DEFAULT_GROUP
refresh-enabled: true
file-extension: yaml
shared-configs:
- data-id: shared-common.yaml
group: DEFAULT_GROUP
refresh: true
discovery:
server-addr: nacos:8848
namespace: public
rabbitmq:
host: rabbitmq
port: 5672
username: guest
password: guest
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
user-service-dev.yaml # 配置文件
server:
port: 9002
spring:
application:
name: user-service
profiles:
active: dev
datasource:
url: jdbc:mysql://mysql:3306/user_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
cloud:
nacos:
config:
server-addr: nacos:8848
namespace: public
group: DEFAULT_GROUP
refresh-enabled: true
file-extension: yaml
shared-configs:
- data-id: shared-common.yaml
group: DEFAULT_GROUP
refresh: true
discovery:
server-addr: nacos:8848
namespace: public
zipkin:
base-url: http://zipkin:9411
enabled: true
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
user-service-pro.yaml # 配置文件
server:
port: 9003
spring:
application:
name: user-service
profiles:
active: pro
datasource:
url: jdbc:mysql://mysql:3306/user_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
cloud:
nacos:
config:
server-addr: nacos:8848
namespace: public
group: DEFAULT_GROUP
refresh-enabled: true
file-extension: yaml
shared-configs:
- data-id: shared-common.yaml
group: DEFAULT_GROUP
refresh: true
discovery:
server-addr: nacos:8848
namespace: public
zipkin:
base-url: http://zipkin:9411
enabled: true
|
docker 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
docker-compose.yml # 配置文件
version: '3'
services:
nacos:
image: nacos/nacos-server:v2.3.0
platform: linux/amd64
ports:
- "8848:8848"
- "9848:9848"
environment:
MODE: standalone
SPRING_DATASOURCE_PLATFORM: mysql
MYSQL_SERVICE_HOST: mysql
MYSQL_SERVICE_PORT: 3306
MYSQL_SERVICE_USER: root
MYSQL_SERVICE_PASSWORD: root
MYSQL_SERVICE_DB_PARAM: characterEncoding=utf8&connectTimeout=2000&allowPublicKeyRetrieval=true&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
MYSQL_SERVICE_DB_NAME: nacos_config
networks:
- cloudtask-net
volumes:
- /Users/yutao/Downloads/sourcecode/cloudtask/docker/jdbc.properties:/home/nacos/init.d/jdbc.properties
restart: always
redis:
image: redis:7.2
networks:
- cloudtask-net
ports:
- "6379:6379"
rabbitmq:
image: rabbitmq:3-management
networks:
- cloudtask-net
ports:
- "15672:15672"
- "5672:5672"
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
ports:
- "3306:3306"
networks:
- cloudtask-net
volumes:
- /Users/yutao/Downloads/devtool/docker/mysqldata:/var/lib/mysql
zipkin:
image: openzipkin/zipkin
container_name: zipkin
networks:
- cloudtask-net
ports:
- "9411:9411"
jenkins:
image: jenkins/jenkins:lts-jdk17
user: root
container_name: jenkins
ports:
- "8080:8080"
- "50000:50000"
volumes:
- /Users/yutao/Downloads/devtool/jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
restart: always
auth-service:
image: auth-service
container_name: auth-service
ports:
- "9000:9000"
depends_on:
- nacos
- mysql
networks:
- cloudtask-net
restart: always
environment:
SPRING_PROFILES_ACTIVE: dev
MANAGEMENT_ZIPKIN_TRACING_ENDPOINT: http://zipkin:9411/api/v2/spans
MANAGEMENT_TRACING_SAMPLING_PROBABILITY: "1.0"
MANAGEMENT_TRACING_ENABLED: "true"
user-service:
image: user-service
container_name: user-service
ports:
- "9001:9001"
depends_on:
- nacos
- mysql
networks:
- cloudtask-net
restart: always
environment:
SPRING_PROFILES_ACTIVE: dev
MANAGEMENT_ZIPKIN_TRACING_ENDPOINT: http://zipkin:9411/api/v2/spans
MANAGEMENT_TRACING_SAMPLING_PROBABILITY: "1.0"
MANAGEMENT_TRACING_ENABLED: "true"
task-service:
image: task-service
container_name: task-service
ports:
- "9002:9002"
depends_on:
- nacos
- mysql
- rabbitmq
- zipkin
networks:
- cloudtask-net
restart: always
environment:
SPRING_PROFILES_ACTIVE: dev
MANAGEMENT_ZIPKIN_TRACING_ENDPOINT: http://zipkin:9411/api/v2/spans
MANAGEMENT_TRACING_SAMPLING_PROBABILITY: "1.0"
MANAGEMENT_TRACING_ENABLED: "true"
gateway-service:
image: gateway-service
container_name: gateway-service
ports:
- "8888:8888"
depends_on:
- auth-service
- user-service
- task-service
networks:
- cloudtask-net
restart: always
environment:
SPRING_PROFILES_ACTIVE: dev
MANAGEMENT_ZIPKIN_TRACING_ENDPOINT: http://zipkin:9411/api/v2/spans
MANAGEMENT_TRACING_SAMPLING_PROBABILITY: "1.0"
MANAGEMENT_TRACING_ENABLED: "true"
networks:
cloudtask-net:
driver: bridge
|
1
2
3
4
5
6
7
|
jdbc.properties #配置文件
spring.sql.init.platform=mysql
db.num=1
db.url.0=jdbc:mysql://mysql:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai
db.user.0=root
db.password.0=root
|
gateway service 模块代码
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package com.yutao;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
application.yml #配置文件
server:
port: 8888
spring:
application:
name: gateway-service
cloud:
nacos:
discovery:
server-addr: nacos:8848
gateway:
# discovery:
# locator:
# enabled: true
routes:
- id: auth
uri: lb://auth-service
predicates:
- Path=/auth/**
filters:
- StripPrefix=1
- id: user
uri: lb://user-service
predicates:
- Path=/users/**
filters:
- StripPrefix=1
- id: task
uri: lb://task-service
predicates:
- Path=/tasks/**
filters:
- StripPrefix=1
zipkin:
base-url: http://zipkin:9411
enabled: true
management:
tracing:
sampling:
probability: 1.0 # 全采样,开发阶段建议设为 1.0
propagation:
type: b3 # 或者使用 w3c,默认也是 b3,保持兼容
logging:
level:
org.springframework.cloud.gateway: DEBUG
reactor.netty: DEBUG
org.springframework.web: DEBUG
org.springframework.http.server.reactive: DEBUG
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
bootstrap.yml # 配置文件
spring:
application:
name: gateway-service
cloud:
nacos:
config:
server-addr: nacos:8848
namespace: public
group: DEFAULT_GROUP
file-extension: yaml
discovery:
server-addr: nacos:8848
namespace: public
profiles:
active: dev
|
1
2
3
4
5
|
FROM eclipse-temurin:21-jdk-alpine
WORKDIR /app
COPY target/gateway-service-*.jar app.jar
EXPOSE 8888
ENTRYPOINT ["java", "-jar", "app.jar"]
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
pom 文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.yutao</groupId>
<artifactId>cloudtask</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.yutao</groupId>
<artifactId>gateway-service</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-resolver-dns-native-macos</artifactId>
<version>4.1.96.Final</version> <!-- 或使用与Spring Boot兼容的版本 -->
<classifier>osx-aarch_64</classifier> <!-- Apple Silicon (M1/M2) -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
|
starter task common 模块代码
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package com.yutao.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnableTaskLog {
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package com.yutao.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Aspect
public class TaskLogAspect {
@Around("@annotation(com.yutao.annotation.EnableTaskLog)")
public Object log(ProceedingJoinPoint pjp) throws Throwable {
log.info("开始执行任务: " + pjp.getSignature());
Object result = pjp.proceed();
log.info("任务结束: " + pjp.getSignature());
return result;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package com.yutao.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import com.yutao.aspect.TaskLogAspect;
@Configuration
@ComponentScan("com.yutao.annotation") // 可选,取决于你的切面是否需要扫描
public class TaskLogAutoConfig {
@Bean
public TaskLogAspect taskLogAspect() {
return new TaskLogAspect();
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
pom 文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.yutao</groupId>
<artifactId>cloudtask</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.yutao</groupId>
<artifactId>starter-task-common</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
</project>
|
task service 模块代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package com.yutao.config;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitConfig {
public static final String TASK_QUEUE = "task-queue";
@Bean
public Queue taskQueue() {
return new Queue(TASK_QUEUE, true); // durable=true
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
package com.yutao.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.yutao.annotation.EnableTaskLog;
import com.yutao.dto.ApiResponse;
import com.yutao.feign.UserClient;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequestMapping("")
public class TaskController {
@Autowired
private UserClient userClient;
@GetMapping("/ping")
@EnableTaskLog
public String ping() {
log.info("Ping 接口请求.");
return "Task service is running.";
}
@GetMapping("/user-info/{id}")
public ApiResponse<?> getUserInfo(@PathVariable Long id) {
log.info("user-info接口请求: {}", id);
return userClient.getUserById(id);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package com.yutao.listener;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class TaskConsumer {
@RabbitListener(queues = "task-queue")
public void consume(String messageString) {
log.info("收到原始JSON字符串: {}", messageString);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package com.yutao;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients(basePackages = "com.yutao.feign") // 指向你的 feign 接口包
public class TaskApplication {
public static void main(String[] args) {
SpringApplication.run(TaskApplication.class, args);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
application.yml #配置文件
server:
port: 8003
spring:
application:
name: task-service
cloud:
nacos:
discovery:
server-addr: nacos:8848
zipkin:
base-url: http://zipkin:9411
enabled: true
rabbitmq:
host: rabbitmq
port: 5672
username: guest
password: guest
data:
redis:
host: redis
port: 6379
jwt:
secret: my_super_secret_key_that_is_at_least_32_chars_long!
expiration: 3600000
management:
tracing:
sampling:
probability: 1.0 # 全采样,开发阶段建议设为 1.0
propagation:
type: b3 # 或者使用 w3c,默认也是 b3,保持兼容
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
bootstrap.yml # 配置文件
spring:
application:
name: task-service
cloud:
nacos:
config:
server-addr: nacos:8848
namespace: public
group: DEFAULT_GROUP
file-extension: yaml
discovery:
server-addr: nacos:8848
namespace: public
profiles:
active: dev
|
1
2
3
4
5
|
FROM eclipse-temurin:21-jdk-alpine
WORKDIR /app
COPY target/task-service-*.jar app.jar
EXPOSE 9002
ENTRYPOINT ["java", "-jar", "app.jar"]
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
pom 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.yutao</groupId>
<artifactId>cloudtask</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.yutao</groupId>
<artifactId>task-service</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.yutao</groupId>
<artifactId>starter-task-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.yutao</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.yutao</groupId>
<artifactId>starter-task-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.yutao</groupId>
<artifactId>user-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
|
user api 模块代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package com.yutao.dto;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class LoginDTO {
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
private String password;
}
|
1
2
3
4
5
6
7
8
9
|
package com.yutao.dto;
import lombok.Data;
@Data
public class RegisterDTO {
private String username;
private String password;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package com.yutao.dto;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserDTO implements Serializable {
private Long id;
private String username;
private String password;
private String email;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package com.yutao.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import com.yutao.dto.ApiResponse;
import com.yutao.dto.RegisterDTO;
import com.yutao.dto.UserDTO;
@FeignClient(name = "user-service")
public interface UserClient {
@GetMapping("/{id}")
ApiResponse<UserDTO> getUserById(@PathVariable("id") Long id);
@PostMapping("/create")
ApiResponse<UserDTO> createUser(@RequestBody RegisterDTO userDTO);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
pom 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.yutao</groupId>
<artifactId>cloudtask</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.yutao</groupId>
<artifactId>user-api</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.yutao</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
</dependencies>
</project>
|
user service 模块代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package com.yutao.config;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitConfig {
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
template.setMessageConverter(jsonMessageConverter());
return template;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
package com.yutao.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.yutao.dto.ApiResponse;
import com.yutao.dto.RegisterDTO;
import com.yutao.dto.UserDTO;
import com.yutao.service.UserService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequestMapping("")
public class UserController {
@Autowired
private UserService userService;
@GetMapping
public ApiResponse<?> findAll() {
log.info("查询所有用户接口");
List<UserDTO> uList = userService.findAll();
return ApiResponse.success(uList);
}
@PostMapping("/create")
public ApiResponse<UserDTO> createUser(@RequestBody RegisterDTO dto) {
log.info("创建用户接口请求参数: {}", dto.getUsername());
UserDTO savedUser = userService.createUser(dto);
return ApiResponse.success(savedUser);
}
@GetMapping("/{id}")
public ApiResponse<UserDTO> getUserById(@PathVariable Long id) {
log.info("查询用户接口请求参数: {}", id);
UserDTO user = userService.getUserById(id);
return ApiResponse.success(user);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package com.yutao.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id @GeneratedValue
private Long id;
private String username;
private String password;
private String email;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package com.yutao.messaging;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.yutao.entity.User;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class UserProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendUserRegisteredMessage(User user) {
try {
// 发送User对象,RabbitTemplate默认使用Jackson2JsonMessageConverter进行序列化
rabbitTemplate.convertAndSend("task-queue", user);
log.info("发送用户注册消息,用户ID: {}", user.getId());
} catch (Exception e) {
log.error("发送用户注册消息失败", e);
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
|
package com.yutao.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.yutao.entity.User;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
boolean existsByUsername(String username);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
package com.yutao.service;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.yutao.dto.RegisterDTO;
import com.yutao.dto.UserDTO;
import com.yutao.entity.User;
import com.yutao.exception.ServiceException;
import com.yutao.messaging.UserProducer;
import com.yutao.repository.UserRepository;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private UserProducer userProducer;
public UserDTO createUser(RegisterDTO dto) {
log.info("接收到注册请求: {}", dto.getUsername());
if (userRepository.existsByUsername(dto.getUsername())) {
throw new ServiceException("用户名已存在");
}
User user = new User();
user.setUsername(dto.getUsername());
user.setPassword(dto.getPassword());
user.setEmail("默认@gmail.com"); // 或默认值
user = userRepository.save(user); // 假设你用 JPA
// 然后再发送注册成功的消息
userProducer.sendUserRegisteredMessage(user);
UserDTO userDTO = new UserDTO();
BeanUtils.copyProperties(user, userDTO);
return userDTO;
}
@Cacheable(value = "user", key = "#id")
public UserDTO getUserById(Long id) {
log.info("UserById接口请求参数: {}", id);
User user = userRepository.findById(id)
.orElseThrow(() -> new ServiceException("用户不存在"));
UserDTO userDTO = new UserDTO();
BeanUtils.copyProperties(user, userDTO);
return userDTO;
}
@Cacheable(value = "userList", key = "'all'")
public List<UserDTO> findAll() {
log.info("查询所有用户接口请求参数: {}");
List<User> users = userRepository.findAll();
return users.stream()
.map(user -> {
UserDTO userDTO = new UserDTO();
BeanUtils.copyProperties(user, userDTO);
return userDTO;
})
.toList();
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package com.yutao;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
@EnableCaching // 加到启动类上
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
application.yml # 配置文件
server:
port: 8002
spring:
application:
name: user-service
cloud:
nacos:
config:
server-addr: nacos:8848
namespace: public
group: DEFAULT_GROUP
file-extension: yaml
discovery:
server-addr: nacos:8848
datasource:
url: jdbc:mysql://mysql:3306/user_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
database-platform: org.hibernate.dialect.MySQL8Dialect
cache:
type: redis
data:
redis:
host: redis
port: 6379
zipkin:
base-url: http://zipkin:9411
enabled: true
rabbitmq:
host: rabbitmq
port: 5672
username: guest
password: guest
jwt:
secret: my_super_secret_key_that_is_at_least_32_chars_long!
expiration: 3600000
management:
tracing:
sampling:
probability: 1.0 # 全采样,开发阶段建议设为 1.0
propagation:
type: b3 # 或者使用 w3c,默认也是 b3,保持兼容
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
bootstrap.yml # 配置文件
spring:
application:
name: user-service
cloud:
nacos:
config:
server-addr: nacos:8848
namespace: public
group: DEFAULT_GROUP
file-extension: yaml
discovery:
server-addr: nacos:8848
namespace: public
rabbitmq:
host: rabbitmq
port: 5672
username: guest
password: guest
profiles:
active: dev
|
1
2
3
4
5
|
FROM eclipse-temurin:21-jdk-alpine
WORKDIR /app
COPY target/user-service-*.jar app.jar
EXPOSE 9001
ENTRYPOINT ["java", "-jar", "app.jar"]
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
pom 文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.yutao</groupId>
<artifactId>cloudtask</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.yutao</groupId>
<artifactId>user-service</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.yutao</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>com.yutao</groupId>
<artifactId>user-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- Redis 支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Spring 缓存注解支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
|
Jenkinsfile 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
pipeline {
agent any
tools {
jdk 'jdk21' // 你在 Jenkins -> Global Tool Configuration 中配置的名字
maven 'maven-3.9.9' // 同上
}
environment {
IMAGE_TAG = "latest"
}
stages {
stage('Build') {
steps {
// 给 mvnw 添加执行权限,防止执行失败(如果用了 mvnw)
sh 'chmod +x mvnw || true'
// 构建所有模块
sh 'mvn clean package -DskipTests'
}
}
stage('Docker Build') {
steps {
script {
def modules = ['auth-service', 'gateway-service', 'task-service', 'user-service']
for (module in modules) {
echo "▶ Building Docker image for ${module}"
sh "docker build -t ${module}:${IMAGE_TAG} ./${module}"
}
}
}
}
stage('Deploy') {
steps {
dir('docker') {
sh 'docker-compose up -d'
}
}
}
}
}
|
父 pom 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
|
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- Maven 的模型版本 -->
<modelVersion>4.0.0</modelVersion>
<!-- 项目坐标 -->
<groupId>com.yutao</groupId> <!-- 项目所属组织 -->
<artifactId>cloudtask</artifactId> <!-- 项目构件 ID -->
<version>1.0-SNAPSHOT</version> <!-- 项目版本 -->
<packaging>pom</packaging> <!-- 打包方式:父项目通常为 pom -->
<name>cloudtask</name> <!-- 项目名称 -->
<!-- 模块列表:定义了子模块(子项目) -->
<modules>
<module>auth-service</module> <!-- 鉴权微服务 -->
<module>common</module> <!-- 公共模块(工具类、通用配置等) -->
<module>user-service</module> <!-- 用户微服务 -->
<module>task-service</module> <!-- 任务微服务 -->
<module>starter-task-common</module> <!-- 自定义 Spring Boot Starter 模块 -->
<module>gateway-service</module>
<module>user-api</module> <!-- 网关服务模块(Spring Cloud Gateway) -->
</modules>
<!-- 项目属性 -->
<properties>
<java.version>21</java.version> <!-- Java 编译版本 -->
<spring-boot.version>3.1.7</spring-boot.version> <!-- Spring Boot 版本 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- 编码 -->
<spring-cloud.version>2022.0.4</spring-cloud.version> <!-- spring cloud 模块版本 -->
<spring.cloud.alibaba.version>2023.0.3.3</spring.cloud.alibaba.version> <!-- spring cloud alibaba 模块版本 -->
</properties>
<!-- 依赖管理:统一管理各模块使用的依赖版本 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 导入 Spring Boot 官方提供的 BOM(依赖版本管理) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version> ${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 配置处理器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring-boot.version}</version>
<optional>true</optional>
</dependency>
<!-- Alibaba Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${spring.cloud.alibaba.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>${spring.cloud.alibaba.version}</version>
</dependency>
<!-- Spring Cloud Alibaba BOM (需额外仓库)-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 公共依赖:对子模块生效 -->
<dependencies>
<!-- JUnit5 测试核心 API -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<!-- JUnit5 参数化测试支持 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<!-- JUnit5 测试引擎 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
</dependencies>
<!-- 构建相关插件配置 -->
<build>
<pluginManagement>
<plugins>
<!-- Java 编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>${java.version}</release> <!-- 使用 Java 21 特性编译 -->
</configuration>
</plugin>
<!-- 单元测试执行插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
</plugin>
<!-- Spring Boot 打包插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 清理插件(可选) -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.3.1</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
|
总结
Spring Boot 博客系列共 6 篇,涵盖从项目构建、Web开发、数据访问、缓存消息、部署监控、安全认证到微服务准备等内容。后续将开启 Spring Cloud 博客系列,系统讲解微服务架构实现。