本文将深入探讨如何高效连通SpringBoot后端、Vue前端和MySQL数据库,构建企业级全栈应用。基于2025年最新技术实践,包含8大核心模块、15个代码示例和3种可视化图表,助你掌握前后端分离架构的精髓。
组件 | 推荐版本 | 主要作用 |
---|---|---|
JDK | 1.8+ | Java运行环境 |
Node.js | 18.x LTS | Vue运行环境 |
Vue | 3.x | 前端框架 |
SpringBoot | 3.x | 后端框架 |
MySQL | 8.0+ | 关系型数据库 |
使用Spring Initializr创建项目时需包含以下关键依赖:
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>com.mysqlgroupId>
<artifactId>mysql-connector-jartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
dependencies>
在application.properties
中配置数据库连接:
# 数据库连接配置
spring.datasource.url=jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=yourpassword
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# JPA配置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
@Entity
@Table(name = "users")
@Data // Lombok注解自动生成getter/setter
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 50)
private String username;
@Column(nullable = false, unique = true)
private String email;
@Column(name = "created_at", updatable = false)
@CreationTimestamp
private LocalDateTime createdAt;
}
@Repository
public interface UserRepository extends JpaRepository {
// 自定义查询方法
Optional findByEmail(String email);
@Query("SELECT u FROM User u WHERE u.username LIKE %:keyword%")
List searchByKeyword(@Param("keyword") String keyword);
}
@Service
@RequiredArgsConstructor // Lombok自动注入
public class UserService {
private final UserRepository userRepository;
public User createUser(UserDTO userDTO) {
if (userRepository.existsByEmail(userDTO.getEmail())) {
throw new BusinessException("邮箱已存在");
}
User user = new User();
user.setUsername(userDTO.getUsername());
user.setEmail(userDTO.getEmail());
return userRepository.save(user);
}
public Page getUsers(int page, int size) {
return userRepository.findAll(PageRequest.of(page, size));
}
}
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping
public ResponseEntity createUser(@Valid @RequestBody UserDTO userDTO) {
User newUser = userService.createUser(userDTO);
return ResponseEntity.status(HttpStatus.CREATED).body(newUser);
}
@GetMapping
public ResponseEntity> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
return ResponseEntity.ok(userService.getUsers(page, size));
}
@GetMapping("/{id}")
public ResponseEntity getUserById(@PathVariable Long id) {
return userService.getUserById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
}
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ResponseEntity handleBusinessException(BusinessException ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.BAD_REQUEST.value(),
ex.getMessage(),
System.currentTimeMillis()
);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach(error -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
ErrorResponse error = new ErrorResponse(
HttpStatus.BAD_REQUEST.value(),
"参数校验失败",
System.currentTimeMillis(),
errors
);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
}
// src/utils/request.js
import axios from 'axios';
const service = axios.create({
baseURL: 'http://localhost:8080/api', // 后端API基础路径
timeout: 10000, // 请求超时时间
});
// 请求拦截器
service.interceptors.request.use(
config => {
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
},
error => {
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use(
response => {
return response.data;
},
error => {
// 统一处理错误
if (error.response) {
const { status, data } = error.response;
switch (status) {
case 401:
router.push('/login');
break;
case 403:
Message.error('无访问权限');
break;
case 404:
Message.error('资源不存在');
break;
default:
Message.error(data.message || '服务异常');
}
}
return Promise.reject(error);
}
);
export default service;
// src/api/user.js
import request from '@/utils/request';
export default {
// 获取用户列表
getUsers(params) {
return request({
url: '/users',
method: 'get',
params
});
},
// 创建用户
createUser(data) {
return request({
url: '/users',
method: 'post',
data
});
},
// 获取用户详情
getUser(id) {
return request({
url: `/users/${id}`,
method: 'get'
});
}
}
详情
// store/modules/user.js
const state = {
userList: [],
pagination: {
current: 1,
size: 10,
total: 0
},
loading: false
};
const mutations = {
SET_USER_LIST(state, list) {
state.userList = list;
},
SET_PAGINATION(state, pagination) {
state.pagination = pagination;
},
SET_LOADING(state, loading) {
state.loading = loading;
}
};
const actions = {
async fetchUsers({ commit, state }) {
try {
commit('SET_LOADING', true);
const params = {
page: state.pagination.current - 1,
size: state.pagination.size
};
const response = await userApi.getUsers(params);
commit('SET_USER_LIST', response.content);
commit('SET_PAGINATION', {
...state.pagination,
total: response.totalElements
});
} catch (error) {
console.error('获取用户列表失败:', error);
} finally {
commit('SET_LOADING', false);
}
}
};
export default {
namespaced: true,
state,
mutations,
actions
};
// Spring Boot跨域配置类
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*") // 允许所有源
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthFilter;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
策略 | 实现方式 | 优点 | 缺点 |
---|---|---|---|
URI版本控制 | /api/v1/users |
直观、易于实现 | 污染URI空间 |
参数版本控制 | /api/users?version=1 |
不改变URI结构 | 需要额外处理参数 |
Header版本控制 | Accept: application/vnd.myapp.v1+json |
最规范,不改变URI和参数 | 客户端实现较复杂 |
// 使用Micrometer监控接口性能
@Bean
public MeterRegistryCustomizer metricsCommonTags() {
return registry -> registry.config().commonTags(
"application", "springboot-vue-demo"
);
}
// 在Controller方法上添加计时器
@GetMapping("/users")
@Timed(value = "user.get.all", description = "获取所有用户的时间")
public ResponseEntity> getAllUsers() {
// ...
}
CREATE TABLE `users` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`username` VARCHAR(50) NOT NULL COMMENT '用户名',
`email` VARCHAR(100) NOT NULL COMMENT '邮箱',
`password` VARCHAR(100) NOT NULL COMMENT '加密密码',
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1-正常,0-禁用',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_email` (`email`),
KEY `idx_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
索引类型 | 适用场景 | 示例 |
---|---|---|
主键索引 | 唯一标识记录,快速定位 | PRIMARY KEY (id) |
唯一索引 | 确保字段唯一性 | UNIQUE KEY (email) |
普通索引 | 提高WHERE和JOIN查询性能 | INDEX (username) |
联合索引 | 多条件查询优化 | INDEX (status, created_at) |
全文索引 | 文本内容搜索优化 | FULLTEXT (content) |
@Service
public class OrderService {
private final UserRepository userRepository;
private final OrderRepository orderRepository;
@Transactional
public void createOrder(OrderDTO orderDTO) {
// 检查用户状态
User user = userRepository.findById(orderDTO.getUserId())
.orElseThrow(() -> new BusinessException("用户不存在"));
if (user.getStatus() != 1) {
throw new BusinessException("用户状态异常");
}
// 扣减用户余额
BigDecimal newBalance = user.getBalance().subtract(orderDTO.getAmount());
if (newBalance.compareTo(BigDecimal.ZERO) < 0) {
throw new BusinessException("余额不足");
}
user.setBalance(newBalance);
userRepository.save(user);
// 创建订单
Order order = new Order();
order.setUserId(user.getId());
order.setAmount(orderDTO.getAmount());
order.setStatus(1);
orderRepository.save(order);
}
}
# HikariCP连接池配置
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=600000
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.auto-commit=true
{
"info": {
"_postman_id": "d2e3f8a4-1a2b-4c3d-5e6f-7a8b9c0d1e2f",
"name": "用户管理API",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "创建用户",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"username\": \"testuser\",\n \"email\": \"[email protected]\"\n}"
},
"url": {
"raw": "http://localhost:8080/api/users",
"protocol": "http",
"host": ["localhost"],
"port": "8080",
"path": ["api", "users"]
}
},
"response": []
},
{
"name": "获取用户列表",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:8080/api/users?page=0&size=10",
"protocol": "http",
"host": ["localhost"],
"port": "8080",
"path": ["api", "users"],
"query": [
{"key": "page", "value": "0"},
{"key": "size", "value": "10"}
]
}
},
"response": []
}
]
}
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("SpringBoot+Vue+MySQL API文档")
.version("1.0")
.description("前后端分离项目接口文档"))
.externalDocs(new ExternalDocumentation()
.description("项目GitHub仓库")
.url("https://github.com/yourproject"));
}
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.group("public")
.pathsToMatch("/api/**")
.build();
}
}
server {
listen 80;
server_name yourdomain.com;
# 前端静态资源
location / {
root /var/www/frontend/dist;
try_files $uri $uri/ /index.html;
}
# 后端API代理
location /api {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 超时设置
proxy_connect_timeout 60s;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public";
}
}
# 每日全量备份
0 2 * * * mysqldump -u root -p'password' --all-databases | gzip > /backup/mysql/all_$(date +\%Y\%m\%d).sql.gz
# 保留最近7天备份
0 3 * * * find /backup/mysql/ -type f -name "*.gz" -mtime +7 -exec rm {} \;
扩展方向 | 技术方案 | 适用场景 |
---|---|---|
水平扩展 | Kubernetes集群部署 | 高并发、高可用场景 |
微服务化 | Spring Cloud Alibaba | 复杂业务系统拆分 |
读写分离 | MySQL主从复制+ShardingSphere | 读多写少场景 |
缓存优化 | Redis缓存热点数据 | 频繁读取的静态数据 |
现象 | 可能原因 | 解决方案 |
---|---|---|
OPTIONS请求返回403 | Spring Security拦截 | 配置Security允许OPTIONS请求 |
缺少CORS响应头 | 未配置CORS | 添加CorsFilter或注解配置 |
携带Cookie时认证失败 | 未设置allowCredentials | 前后端同时设置withCredentials |
部分接口跨域部分不跨域 | 路径匹配问题 | 检查CORS配置的路径匹配规则 |
问题现象:
HikariPool-1 - Connection is not available, request timed out after 30000ms
Communications link failure
解决方案:
增加连接池大小:
spring.datasource.hikari.maximum-pool-size=30
调整超时时间:
spring.datasource.hikari.connection-timeout=60000
添加连接保活配置:
spring.datasource.hikari.keepaliveTime=30000
spring.datasource.hikari.maxLifetime=1800000
优化慢SQL查询
典型问题:
解决方案:
统一使用时间戳传输
配置全局序列化规则:
@Configuration
public class JacksonConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
return builder -> {
builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ISO_DATE_TIME));
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
};
}
}
大数字转为字符串传输:
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long id;
接口类型 | 路径规范 | HTTP方法 | 示例 |
---|---|---|---|
新增资源 | /{resource} | POST | POST /api/services |
查询单个 | /{resource}/{id} | GET | GET /api/services/123 |
查询列表 | /{resource} | GET | GET /api/services |
更新资源 | /{resource}/{id} | PUT | PUT /api/services/123 |
删除资源 | /{resource}/{id} | DELETE | DELETE /api/services/123 |
场景 | 优化前QPS | 优化后QPS | 提升幅度 |
---|---|---|---|
用户列表查询 | 120 | 850 | 608% |
服务详情获取 | 200 | 1500 | 650% |
订单创建 | 80 | 300 | 275% |
优化措施:
本文全面介绍了SpringBoot+Vue+MySQL全栈开发中的核心技术和实践方案,重点涵盖了:
希望能够帮助大家全面掌握前后端分离项目的开发、对接和优化技巧。建议结合实际项目需求,灵活运用文中介绍的各种技术方案,构建高性能、易维护的全栈应用。