目录

Spring Boot+Vue+MySQL 全栈开发技术实施心得与落地实践

一、开发环境搭建与项目初始化

1.1 后端 Spring Boot 项目搭建

技术选型

初始化步骤(IDEA)

1.2 前端 Vue 项目搭建

技术选型

初始化步骤(命令行)

配置.env 文件(前端根目录)

1.3 数据库设计规范

二、核心功能实现(附代码实例)

2.1 用户认证与权限管理(JWT + RBAC)

2.1.1 后端 JWT 实现

2.1.2 前端权限控制

2.2 数据交互核心模块(CRUD 示例)

2.2.1 后端接口实现(用户管理)

2.2.2 前端页面与接口调用

2.3 性能优化实践

2.3.1 数据库优化

2.3.2 缓存优化(Redis 集成)

2.3.3 前端性能优化

三、安全防护要点

3.1 密码安全

3.2 接口安全

3.3 数据安全

四、项目部署与运维

4.1 后端部署(Docker + Jar 包)

4.2 前端部署(Nginx)

4.3 数据库备份

五、总结与展望


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)
  1. 新建 Project → 选择 Spring Initializr → 配置 Group/Artifact/Java 版本
  2. 勾选依赖:Spring Web、Spring Data JPA、MySQL Driver、Lombok、SpringDoc-OpenAPI Starter WebMVC UI
  3. 配置 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 实现
  1. 引入依赖(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>
  1. 编写 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());
    }
}
  1. 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<>();
}
  1. 权限拦截器(基于 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 前端权限控制
  1. 路由守卫(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 后端接口实现(用户管理)
  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();  // 格式化日期
}
  1. 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);
    }
}
  1. 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 前端页面与接口调用
  1. 用户列表页面(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 数据库优化
  1. 索引优化:为频繁查询的字段(如 username、phone)建立索引(已在表结构中配置)
  2. 分页查询优化:使用 JPA 的 Pageable 实现物理分页,避免内存溢出
  3. 批量操作:使用 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 集成)
  1. 后端集成 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>
  1. 配置 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值
  1. 缓存注解使用

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 前端性能优化
  1. 接口请求优化:使用 Axios 拦截器统一处理请求,避免重复代码
  2. 组件懒加载:路由组件使用动态导入(已在路由配置中实现)
  3. 数据缓存:使用 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 包)

  1. 编写 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"]
  1. 构建并运行 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)

  1. 打包前端项目

bash

运行

npm run build
  1. 配置 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 全栈开发的核心在于 "前后端分离、职责清晰":后端专注于数据处理、业务逻辑和安全控制,前端专注于用户交互和体验优化。本文通过实际落地的代码实例,覆盖了项目从初始化到部署的全流程,重点解决了认证授权、数据交互、性能优化、安全防护等关键问题。

后续可扩展方向:

  1. 引入微服务架构(Spring Cloud Alibaba),拆分用户服务、订单服务等,提升系统扩展性;
  2. 集成 WebSocket 实现实时通信(如在线聊天、消息推送);
  3. 接入 AI 能力(Ollama 本地大模型、DeepSeek API),实现智能数据分析、自动生成代码等功能;
  4. 完善监控体系(Prometheus + Grafana),实时监控系统性能和运行状态。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐