从零搭建 SpringBoot 项目详解
SpringBoot 简介与优势
什么是 SpringBoot?
SpringBoot 是基于 Spring 框架的脚手架工具,它简化了基于 Spring 的应用开发。通过自动配置和起步依赖,开发者可以快速创建独立运行、生产级别的 Spring 应用程序。
SpringBoot 的核心优势
- 快速启动:内置 Tomcat、Jetty 等 Web 容器,无需部署 WAR 包
SpringBoot 基于约定大于配置理念简化开发。内容涵盖环境准备(JDK、Maven)、项目创建(Initializr 或手动)、目录结构解析、RESTful API 编写、数据库集成(JPA)、异常处理及单元测试。通过实例演示 Controller、Service、Repository 分层架构,提供配置文件优化与多环境配置方案,帮助开发者快速构建生产级应用并掌握最佳实践。
SpringBoot 是基于 Spring 框架的脚手架工具,它简化了基于 Spring 的应用开发。通过自动配置和起步依赖,开发者可以快速创建独立运行、生产级别的 Spring 应用程序。
SpringBoot 2.x 需要 JDK 1.8 或更高版本,SpringBoot 3.x 需要 JDK 17 或更高版本。
安装步骤:
java -version# 验证 JDK 安装
java -version
javac -version
推荐 IDE:
# 下载 Maven 并解压
# 配置环境变量 MAVEN_HOME 和 PATH
# 验证安装
mvn -version
IntelliJ IDEA 创建步骤:
curl https://start.spring.io/starter.zip \
-ddependencies=web,data-jpa,mysql \
-dgroupId=com.example \
-dartifactId=demo \
-o demo.zip
unzip demo.zip
虽然推荐使用 Spring Initializr,但了解手动创建过程有助于理解项目结构。
my-springboot-project/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/demo/
│ │ └── resources/
│ │ ├── application.properties
│ │ ├── static/
│ │ └── templates/
│ └── test/
│ └── java/
│ └── com/example/demo/
└── 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>
<!-- Spring Boot 父项目,提供依赖管理 -->
<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>
<!-- Web 开发起步依赖 -->
<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>
<!-- Spring Boot Maven 插件 -->
<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;
/**
* SpringBoot 应用主类
* @SpringBootApplication 注解标识这是一个 SpringBoot 应用
*/
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
// 启动 SpringBoot 应用
SpringApplication.run(DemoApplication.class, args);
}
}
src/
├── main/
│ ├── java/ # Java 源代码
│ │ └── com/example/demo/
│ │ ├── controller/ # 控制器层
│ │ ├── service/ # 业务逻辑层
│ │ ├── repository/ # 数据访问层
│ │ ├── entity/ # 实体类
│ │ ├── config/ # 配置类
│ │ └── DemoApplication.java # 主应用类
│ └── resources/ # 资源文件
│ ├── static/ # 静态资源 (css,js,图片)
│ ├── templates/ # 模板文件 (thymeleaf,freemarker)
│ ├── application.properties # 主配置文件
│ └── application-dev.properties # 开发环境配置
└── test/ # 测试代码
└── java/
└── com/example/demo/
application.properties:
# 应用配置
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
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();
}
// Getter 和 Setter 方法
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 组合了@Controller 和@ResponseBody
*/
@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]"));
}
/**
* 获取所有用户
* GET /api/users
*/
@GetMapping
public List<User> getAllUsers() {
return users;
}
/**
* 根据 ID 获取用户
* GET /api/users/{id}
*/
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return users.stream().filter(user -> user.getId().equals(id)).findFirst().orElse(null);
}
/**
* 创建新用户
* POST /api/users
*/
@PostMapping
public User createUser(@RequestBody User user) {
user.setId(counter.incrementAndGet());
users.add(user);
return user;
}
/**
* 更新用户信息
* PUT /api/users/{id}
*/
@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;
}
/**
* 删除用户
* DELETE /api/users/{id}
*/
@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 标识这是一个 Spring 服务组件
*/
@Service
public class UserService {
/**
* 模拟数据库操作 - 获取所有用户
*/
public List<User> findAllUsers() {
// 实际项目中这里会调用 Repository
return List.of(new User(1L, "张三", "[email protected]"), new User(2L, "李四", "[email protected]"));
}
/**
* 根据 ID 查找用户
*/
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);
}
}
<!-- 在 pom.xml 中添加 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- H2 数据库(测试用) -->
<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
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;
}
// Getter 和 Setter
// ... 省略 getter/setter
}
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);
}
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);
}
// 构造方法、getter、setter
public ApiResponse(boolean success, String message, T data, String errorCode) {
this.success = success;
this.message = message;
this.data = data;
this.errorCode = errorCode;
}
// ... 省略 getter/setter
}
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);
}
}
方式一:使用 IDE 运行
方式二:使用 Maven 命令
mvn spring-boot:run
方式三:打包后运行
mvn clean package
java -jar target/demo-1.0.0.jar
使用 Postman 或 curl 测试创建的 API:
# 获取所有用户
curl -X GET http://localhost:8080/api/users
# 根据 ID 获取用户
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));
}
}
使用 YAML 格式配置:
# application.yml
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
# application-dev.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/demo_dev
username: dev_user
password: dev_password
jpa:
show-sql: true
---
# application-prod.yml
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
<!-- 添加依赖 -->
<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]")));
}
}
# 修改端口
server.port=8081
使用 Maven 依赖树分析:
mvn dependency:tree
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
在 IDE 中开启自动编译。
该教程涵盖了以下核心内容:
SpringBoot 的强大之处在于它的'约定大于配置'理念和丰富的 starter 生态。掌握这些基础后,你可以继续深入学习 SpringBoot 的高级特性,如安全认证、消息队列、缓存、监控等,构建更加复杂和强大的应用程序。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online