Spring Boot+Vue+MySQL 全栈开发技术实施心得与落地实践
·
目录
Spring Boot+Vue+MySQL 全栈开发技术实施心得与落地实践
Spring Boot+Vue+MySQL 全栈开发技术实施心得与落地实践
在 Web 应用开发领域,Spring Boot+Vue+MySQL 的技术栈凭借其高效开发、易于维护、扩展性强等优势,成为企业级应用和个人项目的主流选择。本文结合实际项目开发经验,从环境搭建、核心功能实现、性能优化、安全防护等维度,分享全栈开发的实用技巧与落地方案,附可直接运行的代码实例。
一、开发环境搭建与项目初始化
1.1 后端 Spring Boot 项目搭建
技术选型
- 核心框架:Spring Boot 3.2.x(兼容 JDK 17+)
- 数据访问:Spring Data JPA + MySQL 8.0
- 接口文档:SpringDoc-OpenAPI(替代 Swagger)
- 工具依赖:Lombok(简化 POJO)、MapStruct(对象映射)
- 构建工具:Maven 3.8+
初始化步骤(IDEA)
- 新建 Project → 选择 Spring Initializr → 配置 Group/Artifact/Java 版本
- 勾选依赖:Spring Web、Spring Data JPA、MySQL Driver、Lombok、SpringDoc-OpenAPI Starter WebMVC UI
- 配置 application.yml 文件,整合数据库与接口文档:
yaml
spring:
# 数据库配置
datasource:
url: jdbc:mysql://localhost:3306/web_app_db?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
# JPA配置
jpa:
hibernate:
ddl-auto: update # 开发环境使用,生产环境改为none
show-sql: true
properties:
hibernate:
format_sql: true # 格式化SQL日志
database-platform: org.hibernate.dialect.MySQL8Dialect
# SpringDoc-OpenAPI配置
springdoc:
api-docs:
path: /api-docs
swagger-ui:
path: /swagger-ui.html
operationsSorter: method # 按HTTP方法排序接口
1.2 前端 Vue 项目搭建
技术选型
- 核心框架:Vue 3 + Vite 5
- 路由:Vue Router 4
- 状态管理:Pinia
- UI 组件库:Element Plus
- 网络请求:Axios
- 类型校验:TypeScript
初始化步骤(命令行)
bash
运行
# 1. 创建Vue项目(选择TypeScript、Pinia、Axios)
npm create vue@latest web-app-frontend
cd web-app-frontend
npm install
# 2. 安装依赖
npm install element-plus @element-plus/icons-vue axios pinia vue-router
# 3. 配置Axios请求封装(src/utils/request.ts)
import axios from 'axios';
import { ElMessage } from 'element-plus';
const service = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL, // 从.env文件读取基础路径
timeout: 5000
});
# 请求拦截器:添加JWT令牌
service.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
# 响应拦截器:统一错误处理
service.interceptors.response.use(
(response) => response.data,
(error) => {
ElMessage.error(error.response?.data?.msg || '请求失败');
return Promise.reject(error);
}
);
export default service;
配置.env 文件(前端根目录)
env
VITE_API_BASE_URL = http://localhost:8080/api
1.3 数据库设计规范
以用户管理模块为例,设计符合第三范式的 MySQL 表结构:
sql
CREATE TABLE `sys_user` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`username` varchar(50) NOT NULL COMMENT '用户名(唯一)',
`password` varchar(100) NOT NULL COMMENT '加密后的密码',
`nickname` varchar(50) DEFAULT NULL COMMENT '昵称',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`status` tinyint NOT NULL DEFAULT '1' COMMENT '状态:0-禁用,1-正常',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户表';
二、核心功能实现(附代码实例)
2.1 用户认证与权限管理(JWT + RBAC)
2.1.1 后端 JWT 实现
- 引入依赖(pom.xml)
xml
<!-- JWT依赖 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
- 编写 JWT 工具类
java
运行
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Date;
@Component
public class JwtUtils {
@Value("${jwt.secret}")
private String secret; // 密钥(生产环境需配置在环境变量中)
@Value("${jwt.expiration}")
private long expiration; // 令牌过期时间(单位:秒)
// 生成密钥
private SecretKey getSecretKey() {
return Keys.hmacShaKeyFor(secret.getBytes());
}
// 生成JWT令牌
public String generateToken(UserDetails userDetails) {
return Jwts.builder()
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
.signWith(getSecretKey())
.compact();
}
// 验证令牌有效性
public boolean validateToken(String token, UserDetails userDetails) {
String username = extractUsername(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
// 从令牌中提取用户名
public String extractUsername(String token) {
return extractClaims(token).getSubject();
}
// 提取令牌中的声明
private Claims extractClaims(String token) {
return Jwts.parserBuilder()
.setSigningKey(getSecretKey())
.build()
.parseClaimsJws(token)
.getBody();
}
// 检查令牌是否过期
private boolean isTokenExpired(String token) {
return extractClaims(token).getExpiration().before(new Date());
}
}
- RBAC 权限设计(用户 - 角色 - 菜单)
- 实体类设计(User、Role、Menu)
java
运行
// User实体
@Entity
@Table(name = "sys_user")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private String nickname;
// 其他字段...
// 多对多关联角色
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "sys_user_role",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<Role> roles = new HashSet<>();
}
// Role实体
@Entity
@Table(name = "sys_role")
@Data
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String roleName; // 角色标识(如ROLE_ADMIN)
private String roleDesc; // 角色描述
// 多对多关联菜单
@ManyToMany
@JoinTable(
name = "sys_role_menu",
joinColumns = @JoinColumn(name = "role_id"),
inverseJoinColumns = @JoinColumn(name = "menu_id")
)
private Set<Menu> menus = new HashSet<>();
}
- 权限拦截器(基于 Spring Security)
java
运行
@Configuration
@EnableWebSecurity
@EnableMethodSecurity // 开启方法级权限控制
public class SecurityConfig {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtUtils jwtUtils;
// 密码加密方式(BCrypt)
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// 安全配置
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable()) // 前后端分离项目禁用CSRF
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 无状态会话
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/login", "/api/register", "/swagger-ui/**", "/api-docs/**").permitAll() // 公开接口
.anyRequest().authenticated() // 其他接口需认证
)
.addFilterBefore(new JwtAuthenticationFilter(jwtUtils, userDetailsService), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
2.1.2 前端权限控制
- 路由守卫(src/router/index.ts)
typescript
运行
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
import { ElMessage } from 'element-plus';
import { useUserStore } from '@/stores/user';
const routes: RouteRecordRaw[] = [
{
path: '/',
redirect: '/home',
meta: { requiresAuth: true } // 需要登录
},
{
path: '/login',
component: () => import('@/views/Login.vue'),
meta: { requiresAuth: false } // 无需登录
},
{
path: '/home',
component: () => import('@/views/Home.vue'),
meta: { requiresAuth: true, roles: ['ROLE_ADMIN', 'ROLE_USER'] } // 需特定角色
},
{
path: '/admin',
component: () => import('@/views/Admin.vue'),
meta: { requiresAuth: true, roles: ['ROLE_ADMIN'] } // 仅管理员可访问
}
];
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes
});
// 路由守卫:验证登录状态和权限
router.beforeEach((to, from, next) => {
const userStore = useUserStore();
const requiresAuth = to.meta.requiresAuth;
if (requiresAuth) {
const token = localStorage.getItem('token');
if (!token) {
ElMessage.warning('请先登录');
next('/login');
} else {
// 验证角色权限
const userRoles = userStore.roles;
const requiredRoles = to.meta.roles as string[];
if (requiredRoles && !requiredRoles.some(role => userRoles.includes(role))) {
ElMessage.error('无权限访问');
next(from.path);
} else {
next();
}
}
} else {
next();
}
});
export default router;
2.2 数据交互核心模块(CRUD 示例)
2.2.1 后端接口实现(用户管理)
- DTO 设计(数据传输对象)
java
运行
// 用户注册DTO
@Data
public class UserRegisterDTO {
@NotBlank(message = "用户名不能为空")
@Size(min = 4, max = 20, message = "用户名长度为4-20位")
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 6, message = "密码长度不少于6位")
private String password;
private String nickname;
private String email;
}
// 用户响应DTO(隐藏敏感字段)
@Data
@Mapper(componentModel = "spring", uses = {DateMapper.class})
public interface UserResponseDTO {
Long getId();
String getUsername();
String getNickname();
String getEmail();
String getPhone();
Integer getStatus();
String getCreateTime(); // 格式化日期
}
- Service 层实现
java
运行
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final RoleRepository roleRepository;
private final PasswordEncoder passwordEncoder;
private final UserResponseDTOMapper userResponseDTOMapper;
// 用户注册
@Transactional
public UserResponseDTO register(UserRegisterDTO registerDTO) {
// 检查用户名是否已存在
if (userRepository.existsByUsername(registerDTO.getUsername())) {
throw new BusinessException("用户名已存在");
}
// 创建用户并加密密码
User user = new User();
user.setUsername(registerDTO.getUsername());
user.setPassword(passwordEncoder.encode(registerDTO.getPassword()));
user.setNickname(registerDTO.getNickname());
user.setEmail(registerDTO.getEmail());
// 分配默认角色(普通用户)
Role userRole = roleRepository.findByRoleName("ROLE_USER")
.orElseThrow(() -> new BusinessException("默认角色不存在"));
user.setRoles(Collections.singleton(userRole));
// 保存用户
User savedUser = userRepository.save(user);
return userResponseDTOMapper.toDto(savedUser);
}
// 分页查询用户
public Page<UserResponseDTO> getUsers(Integer pageNum, Integer pageSize, String keyword) {
Pageable pageable = PageRequest.of(pageNum - 1, pageSize);
Page<User> userPage;
if (StringUtils.hasText(keyword)) {
userPage = userRepository.findByUsernameContainingOrNicknameContaining(keyword, keyword, pageable);
} else {
userPage = userRepository.findAll(pageable);
}
return userPage.map(userResponseDTOMapper::toDto);
}
}
- Controller 层实现
java
运行
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
// 注册用户
@PostMapping("/register")
public Result<UserResponseDTO> register(@Valid @RequestBody UserRegisterDTO registerDTO) {
UserResponseDTO userDTO = userService.register(registerDTO);
return Result.success(userDTO, "注册成功");
}
// 分页查询用户
@GetMapping
@PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_USER')") // 方法级权限控制
public Result<Page<UserResponseDTO>> getUsers(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String keyword) {
Page<UserResponseDTO> userPage = userService.getUsers(pageNum, pageSize, keyword);
return Result.success(userPage);
}
}
2.2.2 前端页面与接口调用
- 用户列表页面(src/views/UserList.vue)
vue
<template>
<div class="container">
<el-page-header content="用户管理"></el-page-header>
<el-row :gutter="20" class="search-bar">
<el-col :span="8">
<el-input v-model="keyword" placeholder="请输入用户名/昵称" clearable />
</el-col>
<el-col :span="4">
<el-button type="primary" @click="fetchUsers">查询</el-button>
</el-col>
</el-row>
<el-table :data="userList" border style="width: 100%; margin-top: 20px;">
<el-table-column label="ID" prop="id" width="80"></el-table-column>
<el-table-column label="用户名" prop="username"></el-table-column>
<el-table-column label="昵称" prop="nickname"></el-table-column>
<el-table-column label="邮箱" prop="email"></el-table-column>
<el-table-column label="状态" prop="status">
<template #default="scope">
<el-tag :type="scope.row.status === 1 ? 'success' : 'danger'">
{{ scope.row.status === 1 ? '正常' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template #default="scope">
<el-button type="text" @click="editUser(scope.row)">编辑</el-button>
<el-button type="text" danger @click="deleteUser(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-sizes="[5, 10, 20, 50]"
:page-size="pageSize"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
style="margin-top: 20px; text-align: right;"
></el-pagination>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import request from '@/utils/request';
// 分页参数
const pageNum = ref(1);
const pageSize = ref(10);
const total = ref(0);
const keyword = ref('');
const userList = ref([]);
// 获取用户列表
const fetchUsers = async () => {
try {
const res = await request.get('/api/users', {
params: { pageNum, pageSize, keyword: keyword.value }
});
userList.value = res.data.records;
total.value = res.data.total;
} catch (error) {
console.error('获取用户列表失败', error);
}
};
// 页面加载时获取数据
onMounted(() => {
fetchUsers();
});
// 分页大小改变
const handleSizeChange = (val: number) => {
pageSize.value = val;
fetchUsers();
};
// 页码改变
const handleCurrentChange = (val: number) => {
pageNum.value = val;
fetchUsers();
};
// 编辑用户
const editUser = (user: any) => {
// 编辑逻辑(打开弹窗、回显数据等)
console.log('编辑用户', user);
};
// 删除用户
const deleteUser = async (id: number) => {
try {
await request.delete(`/api/users/${id}`);
ElMessage.success('删除成功');
fetchUsers();
} catch (error) {
ElMessage.error('删除失败');
}
};
</script>
<style scoped>
.container {
padding: 20px;
}
.search-bar {
margin-bottom: 10px;
}
</style>
2.3 性能优化实践
2.3.1 数据库优化
- 索引优化:为频繁查询的字段(如 username、phone)建立索引(已在表结构中配置)
- 分页查询优化:使用 JPA 的 Pageable 实现物理分页,避免内存溢出
- 批量操作:使用 JPA 的 bulkUpdate/bulkDelete 或 MyBatis 的批量插入
java
运行
// JPA批量更新示例
@Modifying
@Query("UPDATE User u SET u.status = :status WHERE u.id IN :ids")
int batchUpdateStatus(@Param("status") Integer status, @Param("ids") List<Long> ids);
2.3.2 缓存优化(Redis 集成)
- 后端集成 Redis(Spring Cache)
xml
<!-- Redis依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
- 配置 Redis 缓存(application.yml)
yaml
spring:
redis:
host: localhost
port: 6379
password: # 无密码留空
database: 0
cache:
type: redis
redis:
time-to-live: 3600000 # 缓存过期时间(1小时)
cache-null-values: false # 不缓存null值
- 缓存注解使用
java
运行
@Service
@CacheConfig(cacheNames = "userCache") // 统一缓存名称
public class UserService {
// 缓存查询结果
@Cacheable(key = "#id")
public UserResponseDTO getUserById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new BusinessException("用户不存在"));
return userResponseDTOMapper.toDto(user);
}
// 更新数据后清除缓存
@CacheEvict(key = "#id")
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
// 新增/更新数据后更新缓存
@CachePut(key = "#result.id")
public UserResponseDTO updateUser(UserUpdateDTO updateDTO) {
// 更新逻辑...
return userResponseDTOMapper.toDto(updatedUser);
}
}
2.3.3 前端性能优化
- 接口请求优化:使用 Axios 拦截器统一处理请求,避免重复代码
- 组件懒加载:路由组件使用动态导入(已在路由配置中实现)
- 数据缓存:使用 Pinia 缓存用户信息、字典数据等,减少重复请求
typescript
运行
// src/stores/dict.ts
import { defineStore } from 'pinia';
import request from '@/utils/request';
export const useDictStore = defineStore('dict', {
state: () => ({
dictMap: {} as Record<string, any[]>
}),
actions: {
// 获取字典数据并缓存
async fetchDict(type: string) {
if (this.dictMap[type]) {
return this.dictMap[type]; // 已缓存直接返回
}
const res = await request.get(`/api/dicts/${type}`);
this.dictMap[type] = res.data;
return res.data;
}
}
});
三、安全防护要点
3.1 密码安全
- 后端:使用 BCrypt 加密存储密码(不可逆加密),禁止明文存储
- 前端:密码传输时使用 HTTPS,避免明文传输;登录页面添加验证码防止暴力破解
3.2 接口安全
- 跨域配置:后端配置 CORS 允许指定前端域名访问
java
运行
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:5173") // 前端地址
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
};
}
}
- 接口限流:使用 Spring Cloud Gateway 或自定义拦截器实现接口限流,防止恶意请求
java
运行
// 简单限流拦截器示例
@Component
public class RateLimitInterceptor implements HandlerInterceptor {
// 本地缓存:记录每个IP的请求次数(生产环境建议使用Redis)
private final Map<String, AtomicInteger> ipCountMap = new ConcurrentHashMap<>();
private static final int MAX_REQUESTS = 100; // 每分钟最大请求数
private static final long INTERVAL = 60 * 1000; // 时间窗口(1分钟)
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String ip = request.getRemoteAddr();
AtomicInteger count = ipCountMap.computeIfAbsent(ip, k -> new AtomicInteger(0));
if (count.incrementAndGet() > MAX_REQUESTS) {
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
response.getWriter().write("请求过于频繁,请稍后再试");
return false;
}
// 定时清理过期IP的计数
new Timer().schedule(new TimerTask() {
@Override
public void run() {
ipCountMap.remove(ip);
}
}, INTERVAL);
return true;
}
}
3.3 数据安全
- 输入校验:后端使用 JSR-380 注解(@NotBlank、@Size 等)校验请求参数,防止 SQL 注入、XSS 攻击
- 输出脱敏:敏感数据(如手机号、邮箱)返回时进行脱敏处理
java
运行
// 手机号脱敏示例(MapStruct映射器)
@Mapper(componentModel = "spring")
public interface UserResponseDTOMapper {
@Mapping(target = "phone", expression = "java(desensitizePhone(user.getPhone()))")
UserResponseDTO toDto(User user);
// 手机号脱敏:保留前3位和后4位
default String desensitizePhone(String phone) {
if (phone == null || phone.length() != 11) {
return phone;
}
return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
}
}
四、项目部署与运维
4.1 后端部署(Docker + Jar 包)
- 编写 Dockerfile
dockerfile
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/web-app-backend-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
- 构建并运行 Docker 镜像
bash
运行
# 打包Jar包
mvn clean package -Dmaven.test.skip=true
# 构建Docker镜像
docker build -t web-app-backend:v1.0 .
# 运行容器
docker run -d -p 8080:8080 --name web-app-backend -e SPRING_PROFILES_ACTIVE=prod web-app-backend:v1.0
4.2 前端部署(Nginx)
- 打包前端项目
bash
运行
npm run build
- 配置 Nginx(nginx.conf)
nginx
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html; # 前端打包后的dist目录
index index.html;
try_files $uri $uri/ /index.html; # 解决Vue路由刷新404问题
}
# 接口代理(避免跨域)
location /api {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
4.3 数据库备份
编写定时备份脚本(backup.sh)
bash
运行
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR=/data/backup/mysql
DB_NAME=web_app_db
DB_USER=root
DB_PASS=123456
# 创建备份目录
mkdir -p $BACKUP_DIR
# 备份数据库
mysqldump -u$DB_USER -p$DB_PASS $DB_NAME > $BACKUP_DIR/$DB_NAME-$DATE.sql
# 压缩备份文件
gzip $BACKUP_DIR/$DB_NAME-$DATE.sql
# 删除7天前的备份文件
find $BACKUP_DIR -name "*.sql.gz" -mtime +7 -delete
添加定时任务(crontab)
bash
运行
# 每天凌晨2点执行备份
0 2 * * * /bin/bash /data/backup/mysql/backup.sh
五、总结与展望
Spring Boot+Vue+MySQL 全栈开发的核心在于 "前后端分离、职责清晰":后端专注于数据处理、业务逻辑和安全控制,前端专注于用户交互和体验优化。本文通过实际落地的代码实例,覆盖了项目从初始化到部署的全流程,重点解决了认证授权、数据交互、性能优化、安全防护等关键问题。
后续可扩展方向:
- 引入微服务架构(Spring Cloud Alibaba),拆分用户服务、订单服务等,提升系统扩展性;
- 集成 WebSocket 实现实时通信(如在线聊天、消息推送);
- 接入 AI 能力(Ollama 本地大模型、DeepSeek API),实现智能数据分析、自动生成代码等功能;
- 完善监控体系(Prometheus + Grafana),实时监控系统性能和运行状态。
更多推荐


所有评论(0)