Spring Boot 概述 什么是 Spring Boot Spring Boot 是由 Pivotal 团队提供的基于 Spring 框架的全新框架, 旨在简化 Spring 应用的初始搭建和开发过程。
核心功能
自动配置: 根据 classpath 中的依赖自动配置 Spring 应用
起步依赖: 提供一组便捷的依赖描述符
内嵌服务器: 内置 Tomcat、 Jetty 或 Undertow
生产就绪: 提供监控、 健康检查、 外部化配置等功能
主要优势
快速启动: 几分钟内即可运行 Spring 应用
零配置: 无需 XML 配置, 开箱即用
独立运行: 可打包为可执行 JAR 文件
生态完善: 与 Spring 生态系统无缝集成
Spring Boot 的核心概念 自动配置 Spring Boot 根据 classpath 中的依赖自动配置 Bean。
起步依赖( Starter) 起步依赖是一组方便的依赖描述符, 简化了依赖管理。
Starter
功能
spring-boot-starter-web
Web 开发( Tomcat + Spring MVC)
spring-boot-starter-data-jpa
JPA 数据访问
spring-boot-starter-data-redis
Redis 数据访问
spring-boot-starter-security
Spring Security
spring-boot-starter-test
测试支持
spring-boot-starter-actuator
生产监控
外部化配置 支持多种配置方式, 优先级从高到低:
命令行参数
JNDI 属性
JVM 系统属性
操作系统环境变量
随机值( RandomValuePropertySource)
application-{profile}.properties/yml
application.properties/yml
@PropertySource 注解
默认属性
Spring Boot 的工作原理 启动流程 1 2 3 4 5 6 7 1. 创建 SpringApplication 实例 2. 运行 run() 方法 3. 准备环境( Environment) 4. 创建 ApplicationContext 5. 刷新上下文( 加载 Bean) 6. 调用 CommandLineRunner 和 ApplicationRunner 7. 应用启动完成
自动配置原理 1 2 3 4 5 1. @SpringBootApplication 包含 @EnableAutoConfiguration 2. @EnableAutoConfiguration 导入 AutoConfigurationImportSelector 3. 读取 META-INF/spring.factories 中的自动配置类 4. 根据 @Conditional 条件判断是否生效 5. 注册符合条件的 Bean
快速开始 创建项目 使用 Spring Initializr
在线地址: https://start.spring.io/
步骤:
选择项目类型( Maven/Gradle)
选择语言( Java/Kotlin/Groovy)
填写项目信息( Group、 Artifact、 Name)
选择 Spring Boot 版本
添加依赖( Web、 JPA、 MySQL 等)
生成并下载项目
手动创建 1 2 3 4 5 6 mvn archetype:generate \ -DgroupId=com.example \ -DartifactId=myproject \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DinteractiveMode=false
项目结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 myproject/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/example/myproject/ │ │ │ ├── MyProjectApplication.java # 启动类 │ │ │ ├── controller/ # 控制器 │ │ │ ├── service/ # 业务层 │ │ │ ├── mapper/ # 数据访问层 │ │ │ ├── entity/ # 实体类 │ │ │ └── config/ # 配置类 │ │ └── resources/ │ │ ├── application.yml # 配置文件 │ │ ├── static/ # 静态资源 │ │ └── templates/ # 模板文件 │ └── test/ └── pom.xml
启动类 1 2 3 4 5 6 7 8 9 10 11 12 package com.example.myproject;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication public class MyProjectApplication { public static void main (String[] args) { SpringApplication.run(MyProjectApplication.class, args); } }
第一个 Controller 1 2 3 4 5 6 7 8 9 10 11 12 13 package com.example.myproject.controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;@RestController public class HelloController { @GetMapping("/hello") public String hello () { return "Hello, Spring Boot!" ; } }
运行项目 1 2 3 4 5 6 7 8 9 mvn spring-boot:run mvn clean package java -jar target/myproject-0.0.1-SNAPSHOT.jar curl http://localhost:8080/hello
配置文件 application.properties 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 server.port =8080 server.servlet.context-path =/api spring.datasource.url =jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=Asia/Shanghai spring.datasource.username =root spring.datasource.password =123456 spring.datasource.driver-class-name =com.mysql.cj.jdbc.Driver spring.jpa.hibernate.ddl-auto =update spring.jpa.show-sql =true spring.jpa.properties.hibernate.dialect =org.hibernate.dialect.MySQL5InnoDBDialect logging.level.root =INFO logging.level.com.example =DEBUG logging.file.name =logs/app.log
application.yml( 推荐) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 server: port: 8080 servlet: context-path: /api spring: datasource: url: jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update show-sql: true properties: hibernate: dialect: org.hibernate.dialect.MySQL5InnoDBDialect jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 logging: level: root: INFO com.example: DEBUG file: name: logs/app.log
多环境配置 创建不同环境的配置文件 1 2 3 4 5 src/main/resources/ ├── application.yml # 公共配置 ├── application-dev.yml # 开发环境 ├── application-test.yml # 测试环境 └── application-prod.yml # 生产环境
application-dev.yml 1 2 3 4 5 6 7 8 9 spring: datasource: url: jdbc:mysql://localhost:3306/dev_db username: dev_user password: dev_pass logging: level: com.example: DEBUG
application-prod.yml 1 2 3 4 5 6 7 8 9 spring: datasource: url: jdbc:mysql://prod-server:3306/prod_db username: prod_user password: ${DB_PASSWORD} logging: level: com.example: WARN
激活指定环境 1 2 3 4 5 6 7 8 9 10 spring: profiles: active: dev java -jar app.jar --spring.profiles.active=prod export SPRING_PROFILES_ACTIVE=test
自定义配置 定义配置类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.example.config;import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;@Data @Component @ConfigurationProperties(prefix = "app") public class AppProperties { private String name; private String version; private List<String> admins; private Map<String, String> settings; }
配置文件 1 2 3 4 5 6 7 8 9 app: name: My Application version: 1.0 .0 admins: - admin1@example.com - admin2@example.com settings: theme: dark language: zh-CN
使用配置 1 2 3 4 5 6 7 8 9 10 11 @RestController public class ConfigController { @Autowired private AppProperties appProperties; @GetMapping("/config") public AppProperties getConfig () { return appProperties; } }
Web 开发 RESTful API 基本用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 @RestController @RequestMapping("/api/users") public class UserController { @Autowired private UserService userService; @GetMapping public ResponseEntity<List<User>> list () { List<User> users = userService.findAll(); return ResponseEntity.ok(users); } @GetMapping("/{id}") public ResponseEntity<User> detail (@PathVariable Integer id) { User user = userService.findById(id); return ResponseEntity.ok(user); } @PostMapping public ResponseEntity<User> create (@RequestBody User user) { userService.save(user); return ResponseEntity.status(HttpStatus.CREATED).body(user); } @PutMapping("/{id}") public ResponseEntity<Void> update (@PathVariable Integer id, @RequestBody User user) { user.setId(id); userService.update(user); return ResponseEntity.noContent().build(); } @DeleteMapping("/{id}") public ResponseEntity<Void> delete (@PathVariable Integer id) { userService.delete(id); return ResponseEntity.noContent().build(); } }
统一返回结果 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Data @AllArgsConstructor @NoArgsConstructor public class Result <T> { private Integer code; private String message; private T data; public static <T> Result<T> success (T data) { return new Result <>(200 , "success" , data); } public static <T> Result<T> error (Integer code, String message) { return new Result <>(code, message, null ); } } @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public Result<Void> handleException (Exception e) { return Result.error(500 , e.getMessage()); } }
参数验证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Data public class UserDTO { @NotBlank(message = "用户名不能为空") @Size(min = 2, max = 20, message = "用户名长度必须在2-20之间") private String username; @Email(message = "邮箱格式不正确") private String email; @Min(value = 18, message = "年龄必须大于等于18") private Integer age; } @PostMapping("/users") public Result<User> create (@Valid @RequestBody UserDTO userDTO) { User user = userService.save(userDTO); return Result.success(user); }
数据访问 JPA 添加依赖 1 2 3 4 5 6 7 8 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-jpa</artifactId > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > </dependency >
实体类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Entity @Table(name = "user") @Data public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(nullable = false, length = 50) private String username; @Column(length = 100) private String email; private Integer age; @CreationTimestamp private LocalDateTime createTime; @UpdateTimestamp private LocalDateTime updateTime; }
Repository 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public interface UserRepository extends JpaRepository <User, Integer> { List<User> findByUsername (String username) ; List<User> findByAgeGreaterThan (Integer age) ; @Query("SELECT u FROM User u WHERE u.email LIKE %:email%") List<User> findByEmailContaining (@Param("email") String email) ; @Query(value = "SELECT * FROM user WHERE username = :username", nativeQuery = true) User findByUsernameNative (@Param("username") String username) ; }
Service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Service @Transactional public class UserService { @Autowired private UserRepository userRepository; public List<User> findAll () { return userRepository.findAll(); } public User findById (Integer id) { return userRepository.findById(id) .orElseThrow(() -> new RuntimeException ("用户不存在" )); } public User save (User user) { return userRepository.save(user); } public void delete (Integer id) { userRepository.deleteById(id); } }
MyBatis 添加依赖 1 2 3 4 5 <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter</artifactId > <version > 2.3.0</version > </dependency >
Mapper 接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Mapper public interface UserMapper { @Select("SELECT * FROM user WHERE id = #{id}") User findById (Integer id) ; @Insert("INSERT INTO user(username, email, age) VALUES(#{username}, #{email}, #{age})") @Options(useGeneratedKeys = true, keyProperty = "id") int insert (User user) ; @Update("UPDATE user SET username=#{username}, email=#{email} WHERE id=#{id}") int update (User user) ; @Delete("DELETE FROM user WHERE id = #{id}") int delete (Integer id) ; }
配置文件 1 2 3 4 5 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.entity configuration: map-underscore-to-camel-case: true
安全认证 Spring Security 添加依赖 1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-security</artifactId > </dependency >
基本配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain (HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/public/**" ).permitAll() .requestMatchers("/admin/**" ).hasRole("ADMIN" ) .anyRequest().authenticated() ) .formLogin(form -> form .loginPage("/login" ) .defaultSuccessUrl("/home" ) ) .logout(logout -> logout .logoutUrl("/logout" ) .logoutSuccessUrl("/login" ) ); return http.build(); } @Bean public UserDetailsService userDetailsService () { UserDetails user = User.builder() .username("user" ) .password(passwordEncoder().encode("123456" )) .roles("USER" ) .build(); UserDetails admin = User.builder() .username("admin" ) .password(passwordEncoder().encode("admin123" )) .roles("ADMIN" ) .build(); return new InMemoryUserDetailsManager (user, admin); } @Bean public PasswordEncoder passwordEncoder () { return new BCryptPasswordEncoder (); } }
JWT 认证 添加依赖 1 2 3 4 5 6 7 8 9 10 11 <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 >
JWT 工具类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Component public class JwtUtil { private static final String SECRET_KEY = "your-secret-key" ; private static final long EXPIRATION_TIME = 7 * 24 * 60 * 60 * 1000 ; public String generateToken (String username) { return Jwts.builder() .setSubject(username) .setIssuedAt(new Date ()) .setExpiration(new Date (System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } public String extractUsername (String token) { return Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody() .getSubject(); } public boolean validateToken (String token) { try { Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token); return true ; } catch (Exception e) { return false ; } } }
测试 单元测试 添加依赖 1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency >
Service 测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @SpringBootTest class UserServiceTest { @Autowired private UserService userService; @MockBean private UserRepository userRepository; @Test void testFindById () { User mockUser = new User (); mockUser.setId(1 ); mockUser.setUsername("张三" ); when(userRepository.findById(1 )).thenReturn(Optional.of(mockUser)); User user = userService.findById(1 ); assertNotNull(user); assertEquals("张三" , user.getUsername()); } @Test void testSave () { User user = new User (); user.setUsername("李四" ); user.setEmail("lisi@example.com" ); when(userRepository.save(any(User.class))).thenReturn(user); User savedUser = userService.save(user); assertNotNull(savedUser); assertEquals("李四" , savedUser.getUsername()); } }
Controller 测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 @WebMvcTest(UserController.class) class UserControllerTest { @Autowired private MockMvc mockMvc; @MockBean private UserService userService; @Test void testList () throws Exception { List<User> users = Arrays.asList( new User (1 , "张三" , "zhangsan@example.com" ), new User (2 , "李四" , "lisi@example.com" ) ); when(userService.findAll()).thenReturn(users); mockMvc.perform(get("/api/users" )) .andExpect(status().isOk()) .andExpect(jsonPath("$.length()" ).value(2 )) .andExpect(jsonPath("$[0].username" ).value("张三" )); } @Test void testCreate () throws Exception { User user = new User (); user.setUsername("王五" ); user.setEmail("wangwu@example.com" ); when(userService.save(any(User.class))).thenReturn(user); mockMvc.perform(post("/api/users" ) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(user))) .andExpect(status().isCreated()) .andExpect(jsonPath("$.username" ).value("王五" )); } }
部署 打包 1 2 3 4 5 mvn clean package mvn clean package -DskipTests
运行 1 2 3 4 5 6 7 8 java -jar target/myproject-0.0.1-SNAPSHOT.jar java -jar app.jar --server.port=9090 java -jar app.jar --spring.profiles.active=prod
Docker 部署 Dockerfile 1 2 3 4 5 6 7 8 9 FROM openjdk:11 -jre-slimWORKDIR /app COPY target/myproject-0.0.1-SNAPSHOT.jar app.jar EXPOSE 8080 ENTRYPOINT ["java" , "-jar" , "app.jar" ]
构建和运行 1 2 3 4 5 docker build -t myproject:latest . docker run -d -p 8080:8080 myproject:latest
最佳实践 项目结构规范 1 2 3 4 5 6 7 8 9 10 11 12 13 14 com.example.project/ ├── common/ # 通用模块 │ ├── constant/ # 常量定义 │ ├── exception/ # 异常定义 │ ├── result/ # 统一返回结果 │ └── utils/ # 工具类 ├── config/ # 配置类 ├── controller/ # 控制层 ├── service/ # 业务层 │ ├── impl/ # 业务实现 │ └── dto/ # 数据传输对象 ├── mapper/ # 数据访问层 ├── entity/ # 实体类 └── ProjectApplication.java # 启动类
日志规范 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Slf4j @Service public class UserService { public User findById (Integer id) { log.info("查询用户, ID: {}" , id); try { User user = userMapper.findById(id); log.debug("查询结果: {}" , user); return user; } catch (Exception e) { log.error("查询用户失败, ID: {}" , id, e); throw new BusinessException ("查询用户失败" ); } } }
异常处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Data @AllArgsConstructor public class BusinessException extends RuntimeException { private Integer code; private String message; } @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) public Result<Void> handleBusinessException (BusinessException e) { return Result.error(e.getCode(), e.getMessage()); } @ExceptionHandler(Exception.class) public Result<Void> handleException (Exception e) { log.error("系统异常" , e); return Result.error(500 , "系统异常, 请联系管理员" ); } }
常见问题 端口冲突 1 2 3 4 5 6 问题: Port 8080 was already in use 解决方案: 1. 修改端口: server.port=8081 2. 关闭占用端口的进程 3. 使用随机端口: server.port=0
数据库连接失败 1 2 3 4 5 6 问题: Cannot load driver class: com.mysql.cj.jdbc.Driver 解决方案: 1. 检查 MySQL 驱动依赖是否正确 2. 确认数据库 URL、 用户名、 密码正确 3. 检查数据库服务是否启动
报错处理 💗💗 Spring Boot 报错: BeanCreationException 1 2 3 4 5 6 7 8 9 10 11 12 错误信息: Error creating bean with name 'xxx': Injection of autowired dependencies failed 错误原因: 1. 依赖的 Bean 不存在 2. 循环依赖 3. 构造方法参数不匹配 解决方案: 1. 检查 @Component、 @Service 等注解 2. 使用 @Lazy 解决循环依赖 3. 确认构造方法参数正确
💗💗 Spring Boot 报错: Whitelabel Error Page 1 2 3 4 5 6 7 8 9 10 11 12 错误信息: This application has no explicit mapping for /error 错误原因: 1. 请求路径错误 2. Controller 未正确配置 3. 包扫描范围不对 解决方案: 1. 检查 @RequestMapping 路径 2. 确认 Controller 上有 @RestController 或 @Controller 3. 确保 Controller 在启动类的子包下
学习资源
视频
尚硅谷 SpringBoot 教程: https://www.bilibili.com/video/BV19K4y1L7MT
官方文档
Spring Boot 官方文档: https://spring.io/projects/spring-boot
Spring Boot GitHub: https://github.com/spring-projects/spring-boot
书籍
《 Spring Boot 实战》 : Craig Walls 著
《 Spring Boot 编程思想》 : 小马哥著
教程
Spring Boot 入门教程: https://www.runoob.com/w3cnote/spring-boot-tutorial.html
Baeldung Spring Boot 教程: https://www.baeldung.com/category/spring-boot/
社区
Stack Overflow Spring Boot 标签: https://stackoverflow.com/questions/tagged/spring-boot
Spring 中文社区: https://spring.io/projects