这是一份详细的学习笔记,记录了一个最小的 Spring Boot 后端 + Vue 3 前端项目。项目集成了 MySQL、MyBatis、Redis、Kafka,通过一个完整的"用户注册"业务流程演示各组件的协作。
目录
1. 项目整体架构
1.1 技术架构图
┌─────────────────────────────────────────────────────────────────────────┐
│ 前端 (Vue 3) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ UserManage │───▶│ Pinia │───▶│ Axios │ │
│ │ View │ │ Store │ │ API │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└────────────────────────────────────┬────────────────────────────────────┘
│ HTTP/json
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 后端 (Spring Boot) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Controller │───▶│ Service │───▶│ Mapper │ │
│ │ 用户接口 │ │ 业务逻辑 │ │ 数据访问 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌─────────────┐ ┌─────────────┐ │
│ │ │ Redis │ │ Kafka │ │
│ │ │ 缓存 │ │ 消息队列 │ │
│ │ └─────────────┘ └─────────────┘ │
│ │ │ │
│ └──────────▶│ MySQL │◀─────────┘ │
│ │ 数据库 │ │
│ └───────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
1.2 目录结构
learning-demo/
├── backend/ # Spring Boot 后端
│ ├── pom.xml # Maven 依赖配置
│ └── src/main/
│ ├── java/com/demo/
│ │ ├── DemoApplication.java # 启动类
│ │ ├── config/ # 配置类
│ │ ├── controller/ # 控制器层
│ │ ├── service/ # 服务层
│ │ ├── mapper/ # 数据访问层
│ │ ├── entity/ # 实体类
│ │ ├── dto/ # 数据传输对象
│ │ ├── kafka/ # Kafka 消息处理
│ │ ├── redis/ # Redis 缓存
│ │ └── common/ # 通用类
│ └── resources/
│ ├── application.yml # 应用配置
│ ├── mapper/ # MyBatis XML
│ └── schema.sql # 数据库脚本
│
└── frontend/ # Vue 3 前端
├── package.json # npm 依赖
├── vite.config.js # Vite 配置
└── src/
├── main.js # Vue 入口
├── App.vue # 根组件
├── api/ # API 封装
├── views/ # 页面组件
├── components/ # 通用组件
└── stores/ # 状态管理
2. 后端技术栈详解
2.1 pom.xml - Maven 项目配置
pom.xml 是 Maven 项目的核心配置文件,定义了项目的基本信息、依赖包、构建插件等。Maven 可以管理项目依赖、定义项目的构建配置、管理项目版本和模块。
核心依赖:
| 依赖 | 作用 |
|---|---|
spring-boot-starter-web | Spring MVC、内嵌 Tomcat 服务器 |
mysql-connector-j | MySQL 数据库驱动 |
mybatis-plus-spring-boot3-starter | MyBatis 增强工具 |
spring-boot-starter-data-redis | Redis 缓存支持 |
spring-kafka | Kafka 消息队列支持 |
lombok | 自动生成 getter/setter 等代码 |
2.2 application.yml - 应用配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/learning_demo?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root123
type: com.zaxxer.hikari.HikariDataSource
data:
redis:
host: localhost
port: 6379
kafka:
bootstrap-servers: localhost:9092
consumer:
group-id: demo-consumer-group
enable-auto-commit: false # 手动提交 offset
2.3 DemoApplication.java - 启动类
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@SpringBootApplication 是组合注解,等价于:
@SpringBootConfiguration- 标记配置类@EnableAutoConfiguration- 启用自动配置@ComponentScan- 扫描组件
2.4 User.java - 用户实体类
@Data
@TableName("t_user")
public class User implements Serializable {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
private String email;
private Integer status;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
Lombok 注解:
@Data- 生成 getter/setter/equals/hashCode/toString@TableName- 指定对应的数据库表名@TableId- 标记主键,type = AUTO 表示自增
2.5 UserMapper.java - MyBatis Mapper 接口
@Mapper
@Repository
public interface UserMapper extends BaseMapper<User> {
// BaseMapper 已提供:insert、deleteById、updateById、selectById 等方法
@Select("SELECT * FROM t_user WHERE username = #{username}")
List<User> selectByUsername(String username);
@Delete("DELETE FROM t_user WHERE id = #{id}")
int deleteById(Long id);
}
BaseMapper
2.6 UserCacheService.java - Redis 缓存服务
@Service
public class UserCacheService {
private final RedisTemplate<String, Object> redisTemplate;
public User getUser(Long userId) {
String key = "learning-demo:user:cache:" + userId;
return (User) redisTemplate.opsForValue().get(key);
}
public void setUser(User user) {
String key = "learning-demo:user:cache:" + user.getId();
redisTemplate.opsForValue().set(key, user, 30, TimeUnit.MINUTES);
}
public void deleteUser(Long userId) {
redisTemplate.delete("learning-demo:user:cache:" + userId);
}
}
缓存策略(Cache Aside):
- 查询时先查缓存
- 缓存未命中则查数据库
- 写入/更新/删除时同步更新缓存
2.7 Kafka 生产者和消费者
UserKafkaProducer.java:
@Service
public class UserKafkaProducer {
private final KafkaTemplate<String, String> kafkaTemplate;
public void sendUserRegistrationMessage(User user) {
String message = "{\"userId\":" + user.getId() + ",\"username\":\"" + user.getUsername() + "\"}";
kafkaTemplate.send("user-registration-topic", user.getId().toString(), message);
}
}
UserKafkaConsumer.java:
@Component
public class UserKafkaConsumer {
@KafkaListener(topics = "user-registration-topic", groupId = "demo-group")
public void consume(String message, Acknowledgment ack) {
// 处理消息:发送欢迎邮件、初始化数据等
ack.acknowledge(); // 手动提交 offset
}
}
2.8 UserServiceImpl.java - 用户服务实现
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
private final UserMapper userMapper;
private final UserCacheService userCacheService;
private final UserKafkaProducer userKafkaProducer;
@Override
@Transactional
public UserDTO register(RegisterRequest request) {
// 1. 验证用户名/邮箱是否已存在
// 2. 创建用户对象并保存到数据库
// 3. 发送 Kafka 消息(异步)
// 4. 返回用户信息
userKafkaProducer.sendUserRegistrationMessage(user);
return UserDTO.fromUser(user);
}
@Override
public UserDTO getUserById(Long id) {
// 1. 先查缓存
User cached = userCacheService.getUser(id);
if (cached != null) return UserDTO.fromUser(cached);
// 2. 缓存未命中,查数据库
User user = userMapper.selectById(id);
userCacheService.setUser(user);
return UserDTO.fromUser(user);
}
}
2.9 UserController.java - REST API 控制器
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
@PostMapping("/register")
public Result<UserDTO> register(@RequestBody RegisterRequest request) {
try {
return Result.success(userService.register(request));
} catch (RuntimeException e) {
return Result.error(400, e.getMessage());
}
}
@GetMapping("/{id}")
public Result<UserDTO> getUserById(@PathVariable Long id) {
UserDTO user = userService.getUserById(id);
if (user == null) return Result.error(404, "用户不存在");
return Result.success(user);
}
}
3. 前端技术栈详解
3.1 main.js - Vue 应用入口
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import App from './App.vue'
const app = createApp(App)
app.use(createPinia())
app.use(ElementPlus)
app.mount('#app')
3.2 user.js - API 封装
const request = axios.create({
baseURL: '/api',
timeout: 10000
})
request.interceptors.response.use(response => {
const { code, message, data } = response.data
if (code === 200) return data
ElMessage.error(message)
return Promise.reject(new Error(message))
})
export function register(data) {
return request.post('/users/register', data)
}
export function getUsers(page, pageSize) {
return request.get('/users', { params: { page, pageSize } })
}
3.3 Pinia 状态管理
export const useUserStore = defineStore('user', () => {
const userList = ref([])
const loading = ref(false)
async function fetchUsers(page = 1, pageSize = 10) {
loading.value = true
try {
const data = await getUsers(page, pageSize)
userList.value = data
} finally {
loading.value = false
}
}
return { userList, loading, fetchUsers }
})
4. 业务流程分析
4.1 用户注册流程
用户注册流程:
1. 前端提交表单 POST /api/users/register
│
▼
2. Controller 接收请求
│
▼
3. Service 验证用户名/邮箱
│
▼
4. 保存到 MySQL 数据库
│
▼
5. 发送 Kafka 消息到 user-registration-topic(异步,不阻塞响应)
│
▼
6. 返回成功响应给前端
│
▼
7. Kafka Consumer 接收消息,执行后续业务:
- 发送欢迎邮件(模拟)
- 初始化用户数据(模拟)
4.2 用户查询流程(带缓存)
查询请求
│
▼
查 Redis 缓存 ──── 命中 ──── 直接返回
│
未命中
│
▼
查 MySQL 数据库
│
▼
写入 Redis 缓存(TTL 30分钟)
│
▼
返回数据
5. 数据库设计
CREATE TABLE t_user (
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID',
username VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名',
password VARCHAR(100) NOT NULL COMMENT '密码',
email VARCHAR(100) COMMENT '邮箱',
status TINYINT DEFAULT 1 COMMENT '状态:0-禁用,1-正常',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX idx_email (email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
6. API 接口设计
| 方法 | 路径 | 说明 | 请求体 |
|---|---|---|---|
| POST | /api/users/register | 用户注册 | {username, password, email} |
| GET | /api/users | 获取用户列表 | ?page=1&pageSize=10 |
| GET | /api/users/{id} | 获取单个用户 | - |
| PUT | /api/users/{id} | 更新用户 | {username, email, ...} |
| DELETE | /api/users/{id} | 删除用户 | - |
| GET | /api/users/cache/stats | 缓存统计 | - |
7. 快速启动指南
环境要求
- JDK 17+
- Maven 3.6+
- Node.js 16+
- MySQL 8.0
- Redis 6+
- Kafka 3+
启动步骤
# 1. 创建数据库
mysql -u root -p -e "CREATE DATABASE learning_demo;"
# 2. 启动后端
export JAVA_HOME="/opt/homebrew/opt/openjdk@17"
cd backend
mvn spring-boot:run
# 3. 启动前端
cd frontend
npm install
npm run dev
验证
- 后端 API:http://localhost:8080/api/users
- 前端界面:http://localhost:5173
学习建议
后端学习顺序
DemoApplication.java- 启动类和配置入口User.java- 实体类定义UserMapper.java- 数据库操作UserServiceImpl.java- 核心业务逻辑UserController.java- API 接口UserCacheService.java- 缓存策略UserKafkaProducer/Consumer.java- 消息队列
前端学习顺序
main.js- 应用入口stores/user.js- 状态管理api/user.js- API 封装views/UserManage.vue- 页面组件components/UserForm.vue- 表单组件
项目完整代码已保存在 /Users/hanzi/Desktop/learning-demo/