跳到主要内容
从零搭建 SpringBoot 项目详解 | 极客日志
Java java
从零搭建 SpringBoot 项目详解 SpringBoot 基于约定大于配置理念简化开发。内容涵盖环境准备(JDK、Maven)、项目创建(Initializr 或手动)、目录结构解析、RESTful API 编写、数据库集成(JPA)、异常处理及单元测试。通过实例演示 Controller、Service、Repository 分层架构,提供配置文件优化与多环境配置方案,帮助开发者快速构建生产级应用并掌握最佳实践。
从零搭建 SpringBoot 项目详解
SpringBoot 简介与优势
什么是 SpringBoot?
SpringBoot 是基于 Spring 框架的脚手架工具,它简化了基于 Spring 的应用开发。通过自动配置和起步依赖,开发者可以快速创建独立运行、生产级别的 Spring 应用程序。
SpringBoot 的核心优势
快速启动 :内置 Tomcat、Jetty 等 Web 容器,无需部署 WAR 包
自动配置 :根据 classpath 中的 jar 包自动配置 Bean
起步依赖 :通过 starter 简化 Maven/Gradle 配置
无代码生成 :无需 XML 配置,开箱即用
生产就绪 :提供监控、健康检查等生产级特性
环境准备与工具安装
JDK 安装与配置
SpringBoot 2.x 需要 JDK 1.8 或更高版本,SpringBoot 3.x 需要 JDK 17 或更高版本。
安装步骤:
访问 Oracle 官网或 OpenJDK 官网下载 JDK
安装 JDK 并配置环境变量
验证安装:在命令行执行 java -version
java -version
javac -version
开发工具选择与配置
推荐 IDE:
IntelliJ IDEA(推荐使用 Ultimate 版)
Eclipse with Spring Tools
VS Code with Java 扩展
Maven 安装与配置
mvn -version
数据库准备(可选)
MySQL 5.7+/8.0
PostgreSQL
其他支持的数据库
使用 Spring Initializr 快速创建项目
在线创建项目
访问 start.spring.io
选择项目配置:
Project: Maven Project
Language: Java
Spring Boot: 选择稳定版本(如 3.1.0)
Project Metadata: 填写 Group、Artifact 等信息
Dependencies: 添加需要的起步依赖
使用 IDE 创建
IntelliJ IDEA 创建步骤:
File → New → Project
选择 Spring Initializr
配置项目信息
选择依赖(Web、JPA、MySQL 等)
完成创建
使用命令行创建
curl https://start.spring.io/starter.zip \
-ddependencies=web,data-jpa,mysql \
-dgroupId=com.example \
-dartifactId=demo \
-o demo.zip
unzip demo.zip
手动创建 SpringBoot 项目 虽然推荐使用 Spring Initializr,但了解手动创建过程有助于理解项目结构。
创建 Maven 项目结构 my-springboot-project/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/demo/
│ │ └── resources/
│ │ ├── application.properties
│ │ ├── static/
│ │ └── templates/
│ └── test /
│ └── java/
│ └── com/example/demo/
└── pom.xml
配置 pom.xml <?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 > org.springframework.boot</groupId >
<artifactId > spring-boot-starter-parent</artifactId >
<version > 3.1.0</version >
<relativePath />
</parent >
<groupId > com.example</groupId >
<artifactId > demo</artifactId >
<version > 1.0.0</version >
<packaging > jar</packaging >
<name > demo</name >
<description > Demo project for Spring Boot</description >
<properties >
<java.version > 17</java.version >
</properties >
<dependencies >
<dependency >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-starter-web</artifactId >
</dependency >
<dependency >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-starter-test</artifactId >
<scope > test</scope >
</dependency >
<dependency >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-devtools</artifactId >
<scope > runtime</scope >
<optional > true</optional >
</dependency >
</dependencies >
<build >
<plugins >
<plugin >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-maven-plugin</artifactId >
</plugin >
</plugins >
</build >
</project >
创建主应用类 package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main (String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
项目结构深度解析
标准项目目录结构 src/
├── main/
│ ├── java/
│ │ └── com/example/demo/
│ │ ├── controller/
│ │ ├── service/
│ │ ├── repository/
│ │ ├── entity/
│ │ ├── config/
│ │ └── DemoApplication.java
│ └── resources/
│ ├── static/
│ ├── templates/
│ ├── application.properties
│ └── application-dev.properties
└── test /
└── java/
└── com/example/demo/
配置文件详解 # 应用配置
spring.application.name=demo
# 服务器配置
server.port=8080
server.servlet.context-path=/demo
# 日志配置
logging.level.com.example.demo=DEBUG
logging.file.name=logs/demo.log
# 开发环境配置
spring.profiles.active=dev
application-dev.properties:
# 开发环境数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/demo_dev
spring.datasource.username=dev_user
spring.datasource.password=dev_password
# H2 内存数据库(开发测试用)
# spring.datasource.url=jdbc:h2:mem:testdb
# spring.datasource.driverClassName=org.h2.Driver
# spring.datasource.username=sa
# spring.datasource.password=password
# JPA 配置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
编写第一个 RESTful API
创建实体类 package com.example.demo.entity;
import java.time.LocalDateTime;
public class User {
private Long id;
private String username;
private String email;
private LocalDateTime createTime;
public User () {}
public User (Long id, String username, String email) {
this .id = id;
this .username = username;
this .email = email;
this .createTime = LocalDateTime.now();
}
public Long getId () { return id; }
public void setId (Long id) { this .id = id; }
public String getUsername () { return username; }
public void setUsername (String username) { this .username = username; }
public String getEmail () { return email; }
public void setEmail (String email) { this .email = email; }
public LocalDateTime getCreateTime () { return createTime; }
public void setCreateTime (LocalDateTime createTime) { this .createTime = createTime; }
}
创建控制器 package com.example.demo.controller;
import com.example.demo.entity.User;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
@RestController
@RequestMapping("/api/users")
public class UserController {
private final List<User> users = new ArrayList <>();
private final AtomicLong counter = new AtomicLong ();
public UserController () {
users.add(new User (counter.incrementAndGet(), "张三" , "[email protected] " ));
users.add(new User (counter.incrementAndGet(), "李四" , "[email protected] " ));
}
@GetMapping
public List<User> getAllUsers () {
return users;
}
@GetMapping("/{id}")
public User getUserById (@PathVariable Long id) {
return users.stream().filter(user -> user.getId().equals(id)).findFirst().orElse(null );
}
@PostMapping
public User createUser (@RequestBody User user) {
user.setId(counter.incrementAndGet());
users.add(user);
return user;
}
@PutMapping("/{id}")
public User updateUser (@PathVariable Long id, @RequestBody User userDetails) {
User user = getUserById(id);
if (user != null ) {
user.setUsername(userDetails.getUsername());
user.setEmail(userDetails.getEmail());
}
return user;
}
@DeleteMapping("/{id}")
public String deleteUser (@PathVariable Long id) {
users.removeIf(user -> user.getId().equals(id));
return "用户删除成功" ;
}
}
创建服务层 package com.example.demo.service;
import com.example.demo.entity.User;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class UserService {
public List<User> findAllUsers () {
return List.of(new User (1L , "张三" , "[email protected] " ), new User (2L , "李四" , "[email protected] " ));
}
public Optional<User> findUserById (Long id) {
return findAllUsers().stream().filter(user -> user.getId().equals(id)).findFirst();
}
public User createUser (User user) {
return user;
}
}
更新控制器使用服务层 package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/api/v2/users")
public class UserControllerV2 {
private final UserService userService;
@Autowired
public UserControllerV2 (UserService userService) {
this .userService = userService;
}
@GetMapping
public List<User> getAllUsers () {
return userService.findAllUsers();
}
@GetMapping("/{id}")
public ResponseEntity<User> getUserById (@PathVariable Long id) {
Optional<User> user = userService.findUserById(id);
return user.map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build());
}
@PostMapping
public User createUser (@RequestBody User user) {
return userService.createUser(user);
}
}
数据库集成与 JPA 使用
添加数据库依赖
<dependency >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-starter-data-jpa</artifactId >
</dependency >
<dependency >
<groupId > com.mysql</groupId >
<artifactId > mysql-connector-j</artifactId >
<scope > runtime</scope >
</dependency >
<dependency >
<groupId > com.h2database</groupId >
<artifactId > h2</artifactId >
<scope > runtime</scope >
</dependency >
配置数据源 # application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
# JPA 配置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
# H2 控制台(开发时使用)
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
创建 JPA 实体 package com.example.demo.entity;
import jakarta.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, length = 50)
private String username;
@Column(nullable = false, unique = true, length = 100)
private String email;
@Column(name = "create_time")
private LocalDateTime createTime;
public User () {
this .createTime = LocalDateTime.now();
}
public User (String username, String email) {
this ();
this .username = username;
this .email = email;
}
}
创建 Repository 接口 package com.example.demo.repository;
import com.example.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository <User, Long> {
Optional<User> findByUsername (String username) ;
Optional<User> findByEmail (String email) ;
@Query("SELECT u FROM User u WHERE u.createTime > :createTime")
List<User> findUsersAfterCreateTime (@Param("createTime") LocalDateTime createTime) ;
boolean existsByUsername (String username) ;
boolean existsByEmail (String email) ;
}
更新服务层使用 Repository package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService (UserRepository userRepository) {
this .userRepository = userRepository;
}
public List<User> findAllUsers () {
return userRepository.findAll();
}
public Optional<User> findUserById (Long id) {
return userRepository.findById(id);
}
public User saveUser (User user) {
return userRepository.save(user);
}
public void deleteUser (Long id) {
userRepository.deleteById(id);
}
public boolean existsByUsername (String username) {
return userRepository.existsByUsername(username);
}
public boolean existsByEmail (String email) {
return userRepository.existsByEmail(email);
}
}
异常处理与统一响应
创建自定义异常 package com.example.demo.exception;
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException (String message) {
super (message);
}
}
public class BusinessException extends RuntimeException {
public BusinessException (String message) {
super (message);
}
}
创建统一响应对象 package com.example.demo.dto;
public class ApiResponse <T> {
private boolean success;
private String message;
private T data;
private String errorCode;
public static <T> ApiResponse<T> success (T data) {
return new ApiResponse <>(true , "操作成功" , data, null );
}
public static <T> ApiResponse<T> success (String message, T data) {
return new ApiResponse <>(true , message, data, null );
}
public static <T> ApiResponse<T> error (String message) {
return new ApiResponse <>(false , message, null , null );
}
public static <T> ApiResponse<T> error (String message, String errorCode) {
return new ApiResponse <>(false , message, null , errorCode);
}
public ApiResponse (boolean success, String message, T data, String errorCode) {
this .success = success;
this .message = message;
this .data = data;
this .errorCode = errorCode;
}
}
创建全局异常处理器 package com.example.demo.handler;
import com.example.demo.dto.ApiResponse;
import com.example.demo.exception.ResourceNotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ApiResponse<Object>> handleResourceNotFound (ResourceNotFoundException ex) {
ApiResponse<Object> response = ApiResponse.error(ex.getMessage());
return new ResponseEntity <>(response, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiResponse<Map<String, String>>> handleValidationExceptions (MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap <>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
ApiResponse<Map<String, String>> response = ApiResponse.error("参数验证失败" , "VALIDATION_ERROR" );
response.setData(errors);
return new ResponseEntity <>(response, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ApiResponse<Object>> handleGlobalException (Exception ex) {
ApiResponse<Object> response = ApiResponse.error("服务器内部错误" );
return new ResponseEntity <>(response, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
运行与测试
启动应用程序 mvn clean package
java -jar target/demo-1.0.0.jar
测试 API 接口 使用 Postman 或 curl 测试创建的 API:
curl -X GET http://localhost:8080/api/users
curl -X GET http://localhost:8080/api/users/1
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{"username":"王五","email":"[email protected] "}'
curl -X PUT http://localhost:8080/api/users/1 \
-H "Content-Type: application/json" \
-d '{"username":"张三丰","email":"[email protected] "}'
curl -X DELETE http://localhost:8080/api/users/1
编写单元测试 package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Arrays;
import java.util.Optional;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when ;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(UserControllerV2.class)
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Autowired
private ObjectMapper objectMapper;
@Test
public void testGetAllUsers () throws Exception {
User user1 = new User (1L , "user1" , "[email protected] " );
User user2 = new User (2L , "user2" , "[email protected] " );
when (userService.findAllUsers()).thenReturn(Arrays.asList(user1, user2));
mockMvc.perform(get("/api/v2/users" ))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].username" ).value("user1" ))
.andExpect(jsonPath("$[1].username" ).value("user2" ));
}
@Test
public void testGetUserById () throws Exception {
User user = new User (1L , "testuser" , "[email protected] " );
when (userService.findUserById(1L )).thenReturn(Optional.of(user));
mockMvc.perform(get("/api/v2/users/1" ))
.andExpect(status().isOk())
.andExpect(jsonPath("$.username" ).value("testuser" ));
}
@Test
public void testCreateUser () throws Exception {
User user = new User ("newuser" , "[email protected] " );
User savedUser = new User (1L , "newuser" , "[email protected] " );
when (userService.saveUser(any(User.class))).thenReturn(savedUser);
mockMvc.perform(post("/api/v2/users" ).contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(user)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id" ).value(1L ));
}
}
项目优化与最佳实践
配置文件优化
spring:
application:
name: demo
datasource:
url: jdbc:mysql://localhost:3306/demo
username: root
password: 123456
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
format_sql: true
server:
port: 8080
servlet:
context-path: /demo
logging:
level:
com.example.demo: DEBUG
file:
name: logs/demo.log
多环境配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/demo_dev
username: dev_user
password: dev_password
jpa:
show-sql: true
---
spring:
datasource:
url: jdbc:mysql://prod-db:3306/demo_prod
username: prod_user
password: ${DB_PASSWORD}
jpa:
show-sql: false
logging:
level:
com.example.demo: INFO
添加 Swagger API 文档
<dependency >
<groupId > org.springdoc</groupId >
<artifactId > springdoc-openapi-starter-webmvc-ui</artifactId >
<version > 2.1.0</version >
</dependency >
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI customOpenAPI () {
return new OpenAPI ().info(new Info ().title("Demo API" ).version("1.0" ).description("SpringBoot Demo 项目 API 文档" ).contact(new Contact ().name("开发者" ).email("[email protected] " )));
}
}
常见问题与解决方案
端口被占用
数据库连接失败
检查数据库服务是否启动
验证连接 URL、用户名、密码
检查网络连接
依赖冲突
热部署配置 <dependency >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-devtools</artifactId >
<scope > runtime</scope >
<optional > true</optional >
</dependency >
总结
SpringBoot 项目创建 :多种创建方式及其适用场景
项目结构理解 :标准的 Maven 目录结构和 SpringBoot 约定
核心组件开发 :Controller、Service、Repository 的分层架构
数据库集成 :使用 Spring Data JPA 进行数据持久化
RESTful API 设计 :遵循 REST 原则设计清晰的 API 接口
异常处理 :统一的异常处理和响应格式
测试策略 :单元测试和集成测试的编写
项目优化 :配置文件管理、多环境配置等最佳实践
SpringBoot 的强大之处在于它的'约定大于配置'理念和丰富的 starter 生态。掌握这些基础后,你可以继续深入学习 SpringBoot 的高级特性,如安全认证、消息队列、缓存、监控等,构建更加复杂和强大的应用程序。
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online