Spring Boot 最佳实践
完整学习指南 — 37 个模块全覆盖
基于 Spring Boot 2.7.18 · Java 1.8 · 持续更新中
目录
本指南按照学习路径将 37 个子模块分为 9 个阶段,从入门到高级特性逐一讲解。每个模块包含:原理说明、核心代码、配置要点和注意事项。
| 阶段 | 主题 | 模块数 | 关键技能 |
|---|---|---|---|
| 一、入门 | 快速开始、项目结构 | 3 | Hello World、自动装配原理 |
| 二、Web 开发 | RESTful、响应式、服务器 | 4 | Controller、Filter、API 文档 |
| 三、数据持久化 | SQL/NoSQL/迁移 | 7 | JPA、MyBatis、MongoDB、ES |
| 四、缓存与会话 | Redis/缓存抽象 | 3 | 缓存、分布式 Session |
| 五、消息与邮件 | MQ/Email | 4 | Kafka、RabbitMQ、ActiveMQ |
| 六、安全与监控 | Actuator/Security/Admin | 5 | 监控、加密、AOP |
| 七、日志 | Logback/TinyLog | 2 | 日志配置与替换 |
| 八、高级特性 | 定时/GraalVM/WAR/Starter | 5 | @Scheduled、Native、自定义 Starter |
| 九、测试 | 集成测试 | 1 | MockMvc、@SpringBootTest |
一、入门 — 快速开始
1.1 spring-boot-quick-start
模块定位:这是整个项目的入口示例,展示了一个最简 Spring Boot 应用的结构。它不依赖任何额外的 starter,只继承父项目的核心依赖(spring-boot-starter-web、lombok、devtools),是理解 Spring Boot "约定优于配置"理念的最佳起点。
文件结构
核心代码
Application.java 是一个最简的 Spring Boot 启动类:
package cn.javastack.springboot.quckstart; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
原理说明
@SpringBootApplication 是一个组合注解,等价于以下三个注解的叠加:
| 注解 | 作用 |
|---|---|
@SpringBootConfiguration | 标注当前类为配置类(内部包含 @Configuration) |
@EnableAutoConfiguration | 触发自动装配机制,根据 classpath 中的依赖自动配置 Spring |
@ComponentScan | 自动扫描当前包及子包下的所有 @Component、@Controller 等 |
@SpringBootApplication 所在包开始。如果启动类在 cn.javastack.springboot.quckstart 包下,那么所有子包(如 cn.javastack.springboot.quckstart.controller)下的组件都会被扫描到。如果将启动类放在 cn.javastack 顶级包下,会扫描所有子包。
配置要点
父项目 pom.xml 中已包含以下核心依赖,子模块无需重复声明:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
<optional>true</optional>,这样它只作用于当前模块,不会被传递依赖到依赖此项目的其他模块中。
运行方式
# 方式一:Maven 插件 mvn spring-boot:run -pl spring-boot-quick-start # 方式二:打包后运行 mvn package -pl spring-boot-quick-start java -jar target/spring-boot-quick-start-1.0.jar
启动后默认访问 http://localhost:8080,Spring Boot 会返回 "Whitelabel Error Page"(因为没有定义任何 Controller),这证明了应用已成功启动。
1.2 spring-boot-features — 项目特性
模块定位:展示 Spring Boot 的多个核心特性,包括 FailureAnalyzer(自定义失败分析器)、ApplicationListener(应用监听器)和设计模式的应用。
文件结构
核心代码:FailureAnalyzer(失败分析器)
Spring Boot 在启动失败时会调用所有 FailureAnalyzer 实现类,输出友好的错误信息。这是 Spring Boot 3.x 中重点增强的功能。
package cn.javastack.springboot.features.analyzer; import org.springframework.boot.diagnostics.AbstractFailureAnalyzer; import org.springframework.boot.diagnostics.FailureAnalyzer; public class PortInUseFailureAnalyzer extends AbstractFailureAnalyzer<IllegalArgumentException> { @Override protected void doHandle(IllegalArgumentException failure) { String msg = failure.getMessage(); if (msg != null && msg.contains("Port already in use")) { String port = msg.substring(msg.indexOf("Port") + 5); port = port.substring(0, port.indexOf("bind") - 2); System.err.println("========================================"); System.err.println("端口被占用,请执行以下命令查找并杀死进程:"); System.err.println(" macOS/Linux: lsof -i :" + port); System.err.println(" Windows: netstat -ano | findstr " + port); System.err.println("========================================"); } } }
META-INF/spring.factories(Spring Boot 2.x)或 META-INF/spring/org.springframework.boot.diagnostics.FailureAnalyzer(Spring Boot 3.x)中注册:
org.springframework.boot.diagnostics.FailureAnalyzer=\ cn.javastack.springboot.features.analyzer.PortInUseFailureAnalyzer
核心代码:ApplicationListener(应用监听器)
Spring Boot 应用生命周期中会发布多种事件,通过实现 ApplicationListener 可以监听这些事件:
package cn.javastack.springboot.features.listener; import org.springframework.boot.context.event.ApplicationFailedEvent; import org.springframework.context.ApplicationListener; public class JavastackListener implements ApplicationListener<ApplicationFailedEvent> { @Override public void onApplicationEvent(ApplicationFailedEvent event) { System.err.println("应用启动失败: " + event.getException().getMessage()); } }
常见的事件类型:
| 事件类 | 触发时机 |
|---|---|
ApplicationStartedEvent | 应用启动但尚未处理任何配置时 |
ApplicationReadyEvent | 应用已成功启动并准备好接收请求 |
ApplicationFailedEvent | 应用启动失败时 |
ApplicationStartingEvent | 在应用启动最早阶段(Listener 注册前) |
核心代码:LogService & UserService
这两个类展示了基础的 Service 层写法,作为 Spring Boot 中分层架构的示例:
// LogService.java — 简单日志服务 @Service public class LogService { public void log(String message) { System.out.println("[LOG] " + message); } } // UserService.java — 用户服务示例 @Service public class UserService { @Autowired private LogService logService; public String greet(String name) { logService.log("Greeting: " + name); return "Hello, " + name; } }
@Service 注解本身不包含任何特殊行为,它只是一个语义标记。Spring 会自动将标注了 @Service 的类注册为 Bean。
1.3 spring-boot-application — 应用入口与设计模式
模块定位:展示 Spring Boot 应用入口的多种写法,以及观察者设计模式在 Spring 事件机制中的应用。这是一个"空壳"模块,主要代码量不大,但概念重要。
文件结构
核心代码:观察者模式
Spring 的事件驱动模型基于观察者模式,核心组件:
| 组件 | 对应 Spring 接口 |
|---|---|
| 事件 | ApplicationEvent(继承自 JDK 的 EventObject) |
| 监听器 | ApplicationListener<T> |
| 发布者 | ApplicationEventPublisher |
// 1. 定义自定义事件 public class JavaStackEvent extends ApplicationEvent { private final String message; public JavaStackEvent(Object source, String message) { super(source); this.message = message; } public String getMessage() { return message; } } // 2. 定义监听器 @Component public class ReaderListener implements ApplicationListener<JavaStackEvent> { @Override public void onApplicationEvent(JavaStackEvent event) { System.out.println("收到事件: " + event.getMessage()); } } // 3. 发布事件(在任何 Bean 中) @Autowired private ApplicationEventPublisher publisher; publisher.publishEvent(new JavaStackEvent(this, "Hello Spring!"));
@EventListener 注解可以简化监听器的编写,无需实现接口:
@EventListener public void handleEvent(JavaStackEvent event) { System.out.println(event.getMessage()); }如果需要异步监听,在
@EventListener 上添加 async = true 属性即可。
二、Web 开发 — RESTful Web 服务
2.1 spring-boot-web — 完整的 Web 应用示例
模块定位:这是整个项目中代码量最大的模块之一,涵盖了 Spring Boot Web 开发的几乎所有核心功能:Controller、Filter、Servlet、全局异常处理、自定义 Converter、国际化、错误页面、REST 客户端调用、安全配置等。作为学习材料,它的价值非常高。
文件结构
核心代码:Controller 层
模块包含四种 Controller 写法,覆盖常见场景:
(1) 登录接口 — 传统 Controller
@Controller public class LoginController { @GetMapping("/login") public String login( @RequestParam String username, @RequestParam String password, Model model) { if ("admin".equals(username) && "123456".equals(password)) { return "redirect:/index"; } model.addAttribute("error", "用户名或密码错误"); return "login"; } }
(2) RESTful 接口 — @RestController
@RestController public class ResponseBodyController { @GetMapping("/api/users/{id}") public Result<User> getUser(@PathVariable Long id) { User user = new User(id, "admin", "管理员"); return Result.success(user); } @PostMapping("/api/users") public Result<User> createUser(@RequestBody User user) { return Result.success(user); } }
(3) REST 客户端 — 调用外部 API
@RestController public class CallRestController { @Autowired private RestTemplate restTemplate; @GetMapping("/call-external") public String callExternal() { return restTemplate.getForObject("https://api.example.com/data", String.class); } }
核心代码:全局异常处理
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(RuntimeException.class) public Result<Void> handleRuntimeException(RuntimeException e) { System.err.println("发生异常: " + e.getMessage()); return Result.error("500", "服务器内部错误"); } @ExceptionHandler(MethodArgumentNotValidException.class) public Result<Void> handleValidationException(MethodArgumentNotValidException e) { String msg = e.getBindingResult().getAllErrors().get(0).getDefaultMessage(); return Result.error("400", msg); } }
- 不加任何限定:拦截所有 Controller
@ControllerAdvice("包名"):只拦截指定包下的 Controller@ControllerAdvice(annotations = RestController.class):只拦截 @RestController
核心代码:Filter 与 Servlet
Spring Boot 支持三种注册 Filter/Servlet 的方式:
方式一:注解扫描(最简单)
@WebFilter(urlPatterns = "/api/*", filterName = "apiFilter") public class JavaFilter implements Filter { @Override public void doFilterthrows IOException, ServletException { chain.doFilter(req, res); } }
方式二:Bean 注册(推荐)
@Bean public FilterRegistrationBean<JavaFilter> filterRegistration() { FilterRegistrationBean<JavaFilter> registration = new FilterRegistrationBean(new JavaFilter()); registration.addUrlPatterns("/api/*"); registration.setOrder(1); // 过滤顺序 return registration; }
方式三:WebMvcConfigurer 注册拦截器
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/api/**") .excludePathPatterns("/login"); } }
核心代码:自定义 Converter
用于将前端传来的字符串转换为自定义对象:
public class CustomConverter implements Converter<String, User> { @Override public User convert(String source) { String[] parts = source.split(","); return new User(Long.valueOf(parts[0]), parts[1]); } }
注册到 Spring MVC:
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addFormatters(FormatterRegistry registry) { registry.addConverter(new CustomConverter()); } }
核心代码:国际化(i18n)
资源文件组织:
// index.properties (默认) greeting=Hello welcome=Welcome to our system // index_zh_CN.properties (中文) greeting=你好 welcome=欢迎来到我们的系统
在 Controller 中使用:
@Autowired private MessageSource messageSource; public String getMessage(Locale locale) { return messageSource.getMessage("greeting", null, locale); }
核心代码:Spring Security 配置
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/css/**", "/img/**").permitAll() // 静态资源放行 .anyRequest().authenticated() // 其他需要认证 .and() .formLogin().loginPage("/login").permitAll() // 登录页放行 .and() .logout().permitAll(); } @Bean public UserDetailsService userDetailsService() { UserDetails user = User.withDefaultPasswordEncoder() .username("admin").password("123456").roles("ADMIN").build(); return new InMemoryUserDetailsManager(user); } }
WebSecurityConfigurerAdapter 已被废弃。新的配置方式:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { return http.authorizeHttpRequests(authz -> authz .requestMatchers("/public/**").permitAll() .anyRequest().authenticated()) .formLogin(FormLogin::disable) .build(); }
配置要点:application.yml
server: port: 8080 tomcat: threads: max: 200 min-spare: 10 max-http-header-size: 8KB spring: messages: basename: i18n/common,i18n/index encoding: UTF-8 mvc: throw-exception-if-no-handler-found: true
throw-exception-if-no-handler-found: true:找不到路由时抛出异常而非返回 404 页面,便于全局异常处理server.tomcat.threads.max: 200:根据并发量调整线程池大小,默认 200server.max-http-header-size: 8KB:增大 Header 限制,避免大 Cookie 或 Token 被截断
2.2 spring-boot-webflux — 响应式 Web
模块定位:展示 Spring WebFlux 响应式编程模型,基于 Netty 服务器,非阻塞 I/O,适合高并发场景。
文件结构
核心代码:WebFlux Controller
WebFlux 支持两种编程模型:注解式(与 Spring MVC 语法几乎相同)和函数式。
注解式(本模块采用)
@RestController @RequestMapping("/api") public class CallRestController { @GetMapping("/users/{id}") public Mono<User> getUser(@PathVariable Long id) { return Mono.fromSupplier(() -> userService.findById(id)); } @GetMapping("/users") public Flux<User> getUsers() { return Flux.fromIterable(userService.findAll()); } }
函数式路由(补充写法)
@Configuration public class RouteConfig { @Bean public RouterFunction<ServerResponse> routes(Handler handler) { return route() .GET("/users/{id}", handler::getUser) .GET("/users", handler::getUsers) .build(); } }
核心概念对比:Spring MVC vs WebFlux
| 特性 | Spring MVC | WebFlux |
|---|---|---|
| 运行时 | Servlet 容器(Tomcat/Jetty) | Netty(内置) |
| 模型 | 阻塞式线程池 | 非响应式流(Reactor/RxJava) |
| 返回类型 | User | Mono<User> / Flux<User> |
| 适用场景 | 传统 CRUD、事务操作 | 高并发、流式处理、WebSocket |
| 学习曲线 | 低 | 高(需理解响应式编程) |
Mono<T>:零个或一个元素(0..1)Flux<T>:零个或多个元素(0..N)
2.3 spring-boot-undertow — Undertow 服务器
模块定位:将默认的 Tomcat 替换为 Undertow,一个基于事件驱动的高性能异步 Web 服务器,特别适合高并发场景。JBOSS 出品,性能在某些场景下优于 Tomcat。
核心配置:pom.xml
关键操作是排除 Tomcat,引入 Undertow:
<!-- 排除默认 Tomcat --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <!-- 引入 Undertow --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency>
配置要点:application.yml
server: undertow: threads: io: 4 # IO 线程数(默认 CPU 核心数) worker: 64 # 工作线程数(默认 200) buffer-size: 10240 # 每个缓冲区大小(字节) no-buffering: false # 是否禁用缓冲
- 高并发、大量并发连接(如 WebSocket 长连接场景)
- 需要单节点处理更多连接
- 对内存使用有严格要求(Undertow 内存占用更低)
- 需要 Servlet 规范的全部特性(如某些第三方库依赖)
- 团队对 Tomcat 更熟悉,运维工具链围绕 Tomcat
- 需要部署到外部应用服务器
2.4 spring-boot-knife4j — API 文档
模块定位:集成 Knife4j(基于 Swagger/OpenAPI),自动生成接口文档,支持在线调试。
文件结构
核心代码:接口定义
@Api(tags = "用户接口") @RestController @RequestMapping("/api/user") public class Knife4jController { @ApiOperation("获取用户信息") @ApiImplicitParam(name = "id", value = "用户ID", required = true) @GetMapping("/{id}") public User getUser(@PathVariable Long id) { return userService.findById(id); } @ApiOperation("创建用户") @ApiModel(description = "创建用户请求") @PostMapping public User createUser(@RequestBody @ApiParam("用户信息") User user) { return userService.create(user); } }
核心代码:Knife4j 配置
@Configuration public class Knife4jConfiguration { @Bean public Docket createRestApi() { return new Docket(DocumentationType.OAS_30) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("cn.javastack.springboot.knife4j")) .paths(PathSelectors.ant("/api/**")) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("Spring Boot 最佳实践 — API 文档") .version("1.0") .description("覆盖 Spring Boot 核心功能") .build(); } }
访问 http://localhost:8080/doc.html 即可打开 Knife4j 文档界面。
springdoc-openapi-starter-webmvc-ui),配置方式有所不同:
<!-- Spring Boot 3.x 推荐方案 --> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.0.0</version> </dependency> # 访问 http://localhost:8080/swagger-ui.html
三、数据持久化 — 数据库连接池
3.1 spring-boot-datasource — Druid 连接池
模块定位:使用阿里巴巴 Druid 连接池替代 Spring Boot 默认的数据源配置,提供连接监控、SQL 统计等高级功能。
文件结构
核心代码:Druid 配置
@Configuration public class DsConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource") public DruidDataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); return dataSource; } // 注册 Druid 监控 Servlet @Bean public ServletRegistrationBean<StatViewServlet> druidServlet() { ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean(new StatViewServlet()); bean.addUrlMappings("/druid/*"); return bean; } }
配置要点:application.yml
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 druid: initial-size: 5 min-idle: 5 max-active: 20 max-wait: 60000 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 validation-query: SELECT 1 test-while-idle: true test-on-borrow: false test-on-return: false pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 20
| 参数 | 含义 | 推荐值 |
|---|---|---|
| initial-size | 初始化连接数 | 5(中小型应用) |
| max-active | 最大连接数 | 根据并发量调整,通常 20-100 |
| max-wait | 获取连接最大等待时间(ms) | 60000(60秒) |
| test-while-idle | 空闲时检测连接有效性 | true(必须开启) |
| pool-prepared-statements | 开启 PSCache | true(Oracle/MySQL 5.7+) |
http://localhost:8080/druid 可以看到:
- 数据源配置信息
- 实时 SQL 监控
- URL 监控
- Session 监控
3.2 spring-boot-jpa — Spring Data JPA
模块定位:使用 Spring Data JPA 进行 ORM 操作,是 Spring Boot 中最流行的 SQL 持久化方案。
文件结构
核心代码:实体类
@Entity @Table(name = "t_user") @Data // Lombok public class UserDO { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, length = 50) private String username; @Column(nullable = false) private String password; @Column(length = 100) private String email; @Column(name = "create_time") private LocalDateTime createTime; @PrePersist public void onCreate() { this.createTime = LocalDateTime.now(); } }
核心代码:Repository
public interface UserRepository extends JpaRepository<UserDO, Long> { // 方法名解析:Spring Data 自动实现 Optional<UserDO> findByUsername(String username); // 模糊查询 List<UserDO> findByUsernameLike(String keyword); // 自定义 SQL(复杂查询时使用) @Query("SELECT u FROM UserDO u WHERE u.email = :email") List<UserDO> findByEmail(@Param("email") String email); // 原生 SQL @Query(value = "SELECT * FROM t_user WHERE age > :age", nativeQuery = true) List<UserDO>> findByAgeGt(@Param("age") int age); }
核心代码:Controller
@RestController @RequestMapping("/api/users") public class UserController { @Autowired private UserRepository userRepository; @GetMapping public Page<UserDO> list( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = 10) int size) { return userRepository.findAll(Pageable.ofPage(page, size)); } @PostMapping public UserDO create(@RequestBody UserDO user) { return userRepository.save(user); } }
配置要点:application.yml
spring: datasource: url: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai username: root password: 123456 jpa: hibernate: ddl-auto: update # create/create-update/validate/none show-sql: true # 控制台打印 SQL properties: hibernate: format_sql: true # 格式化 SQL
| 值 | 行为 | 适用场景 |
|---|---|---|
create | 启动时删除所有表,重新创建 | 纯测试环境 |
create-drop | 启动时创建,关闭时删除 | 单元测试 |
update | 只更新表结构,不删除已有数据 | 开发环境 |
validate | 只校验,不执行任何修改 | 生产环境(推荐) |
none | 不做任何操作 | 使用 Flyway/Liquibase 管理迁移 |
3.3 spring-boot-mybatis — MyBatis 集成
模块定位:使用 MyBatis 半自动 ORM 框架,适合需要精细控制 SQL 的场景。
文件结构
核心代码:Mapper 接口
@Mapper public interface UserMapper { @Select("SELECT * FROM t_user WHERE id = #{id}") UserDO selectById(Long id); @Insert("INSERT INTO t_user(username, password, email) VALUES(#{username}, #{password}, #{email})") @Options(useGeneratedKeys = true, keyProperty = "id") int insert(UserDO user); @Update("UPDATE t_user SET username = #{username} WHERE id = #{id}") int update(UserDO user); @Delete("DELETE FROM t_user WHERE id = #{id}") int deleteById(Long id); }
XML 映射方式(更常见)
// UserMapper.java — 接口只声明方法 @Mapper public interface UserMapper { List<UserDO> selectByCondition(UserQuery query); UserDO selectById(Long id); }
<!-- UserMapper.xml --> <mapper namespace="cn.javastack.springboot.mybatis.mapper.UserMapper"> <select id="selectById" resultType="UserDO"> SELECT * FROM t_user WHERE id = <#{id} </select> <select id="selectByCondition" resultType="UserDO"> SELECT * FROM t_user <where> <if test="username != null and username != ''"> AND username LIKE CONCAT('%', #{username}, '%') </if> <if test="email != null and email != ''"> AND email = #{email} </if> </where> ORDER BY create_time DESC </select> </mapper>
配置要点
mybatis: mapper-locations: classpath:mapper/*.xml # XML 文件位置 type-aliases-package: cn.javastack.springboot.mybatis.entity # 实体别名 configuration: map-underscore-to-camel-case: true # 下划线转驼峰 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印 SQL
- SQL 完全可控,适合复杂查询和性能优化
- XML 方式将 SQL 与 Java 代码分离,便于维护
- 支持动态 SQL(
<if>、<where>、<foreach>等标签) - 一级缓存(SqlSession 级别)和二级缓存(Mapper 级别)
3.4 spring-boot-mybatis-plus — MyBatis-Plus 增强
模块定位:MyBatis 的增强框架,在保留 MyBatis 灵活性的同时,提供 CRUD 插件、代码生成器、分页插件等开箱即用的功能。
文件结构
核心代码:实体类(MP 注解)
@TableName("t_user") @Data public class UserDO { @TableId(type = IdType.AUTO) private Long id; @TableField("username") private String username; @TableField(value = "create_time", fill = FieldFill.INSERT) // 插入时自动填充 private LocalDateTime createTime; @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) // 更新时自动填充 private LocalDateTime updateTime; @TableLogic // 逻辑删除标记 private Integer deleted; }
核心代码:自动填充处理器
@Component public class CustomMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } @Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } }
核心代码:Service 层(MP 的强大之处)
// Service 接口 — 继承 MP 的 IService,自动获得 CRUD 方法 public interface UserService extends IService<UserDO> { // 无需声明方法,save/saveBatch/removeById/listByIds 等全部自动获得 } // Service 实现 @Service public class UserServiceImpl extends ServiceImpl<UserMapper, UserDO> implements UserService { // 自定义查询(复杂场景) public Page<UserDO> searchUsers(String keyword, int page, int size) { LambdaQueryWrapper<UserDO> wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.isNotBlank(keyword), UserDO::getUsername, keyword) .orderByDesc(UserDO::getCreateTime); return page(new Page<>(page, size), wrapper); } }
继承 IService 后自动获得的方法:
| 方法 | 说明 |
|---|---|
save(entity) | 保存单条 |
saveBatch(entities) | 批量保存 |
removeById(id) | 根据 ID 删除 |
listByIds(ids) | 根据 ID 列表查询 |
updateById(entity) | 根据 ID 更新 |
getById(id) | 根据 ID 查询 |
page(page, wrapper) | 分页查询 |
- 无侵入:仅增强,不改变 MyBatis 核心机制
- CRUD 插件:80% 的增删改查无需写 SQL
- 代码生成器:一键生成 Entity/Mapper/Service/Controller
- 分页插件:自动拼接 LIMIT 语句
- 条件构造器(Wrapper):链式 API 构建动态查询
3.5 spring-boot-mongodb — MongoDB 集成
模块定位:使用 Spring Data MongoDB 操作 MongoDB 文档数据库。
核心代码
// User.java — 实体类 @Document(collection = "users") @Data public class User { @Id private String id; private String username; private String email; } // UserRepository.java — 数据访问接口 public interface UserRepository extends MongoRepository<User, String> { List<User> findByUsername(String username); } // MongoController.java — 控制层 @RestController @RequestMapping("/api/users") public class MongoController { @Autowired private UserRepository userRepository; @GetMapping public List<User> list() { return userRepository.findAll(); } @PostMapping public User create(@RequestBody User user) { return userRepository.save(user); } }
配置要点
spring: data: mongodb: uri: mongodb://localhost:27017/testdb
3.6 spring-boot-elasticsearch — Elasticsearch 集成
模块定位:使用 Spring Data Elasticsearch 操作 ES 搜索引擎,适用于全文检索、日志分析等场景。
核心代码
// User.java — 文档实体 @Document(indexName = "users", shards = 1, replicas = 0) @Data public class User { @Id private String id; @Field(type = FieldType.Text, analyzer = "ik_max_word") // 中文分词器 private String username; @Field(type = FieldType.Keyword) private String email; } // UserRepository.java public interface UserRepository extends ElasticsearchRepository<User, String> { } // EsController.java — 搜索接口 @RestController public class EsController { @Autowired private UserRepository userRepository; @GetMapping("/api/users/search") public SearchHits<User> search(@RequestParam String keyword) { NativeQuery query = NativeQuery.builder() .withQuery(q -> q.match(m -> m.field("username").keyword(keyword))) .withPageable(PageRequest.of(0, 10)) .build(); return elasticsearchOperations.search(query, User.class); } }
ElasticsearchRepository 已被标记为废弃,推荐使用 ElasticsearchClient
3.7 spring-boot-flyway — 数据库版本迁移
模块定位:使用 Flyway 进行数据库版本管理,在应用启动时自动执行 SQL 迁移脚本,确保数据库结构与代码一致。
文件结构
迁移脚本示例
// V1__create_user_table.sql CREATE TABLE t_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, password VARCHAR(100) NOT NULL, email VARCHAR(100), create_time DATETIME ); // V2__add_user_fields.sql ALTER TABLE t_user ADD COLUMN phone VARCHAR(20), ADD COLUMN status INT DEFAULT 1;
配置要点
spring: flyway: enabled: true # 启用 Flyway(默认即启用) locations: classpath:db/migration # 迁移脚本位置 baseline-on-migrate: true # 对已存在的库,自动基线化 validate-on-migrate: true # 执行前校验
- 文件名格式:
V{版本号}__{描述}.sql,如V1__create_user_table.sql - Flyway 在
flyway_schema_history表中记录已执行的迁移 - 迁移脚本按文件名排序执行,不可修改已执行的脚本
baseline-on-migrate: true:对已有数据库,将其当前状态标记为基线版本
| 特性 | Flyway | Liquibase |
|---|---|---|
| 脚本格式 | 纯 SQL | XML/YAML/SQL |
| 学习成本 | 低 | 中 |
| 回滚支持 | 有限(V7.x+ 支持) | 完整 |
| 社区规模 | 更大 | 较小 |
| 推荐场景 | 大多数项目(推荐) | 需要复杂回滚的企业级场景 |
四、缓存与会话 — Spring Cache
四、1 spring-boot-cache — 缓存抽象
模块定位:使用 Spring Cache 抽象层,统一多种缓存实现(Redis、ConcurrentHashMap 等),通过注解即可实现缓存功能。
核心代码:缓存配置
// 启用缓存注解 @SpringBootApplication @EnableCaching // 关键:开启缓存支持 public class Application { } // CacheConfiguration.java — 缓存管理器配置 @Configuration public class CacheConfiguration { @Bean public CacheManager cacheManager() { RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofMinutes(30)) // 默认过期时间 .serializeKeysWith(SerializationPair.fromSerializer(new StringRedisSerializer())) .serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); return RedisCacheManager.builder(RedisConnectionFactory) .cacheDefaults(config) .build(); } }
核心代码:缓存注解使用
@Service public class CacheService { @Autowired private UserRepository userRepository; // 缓存方法返回值,key 为 "user:" + 参数 username @Cacheable(value = "users", key = "#username") public UserDO getUser(String username) { return userRepository.findByUsername(username).orElse(null); } // 更新缓存 @CachePut(value = "users", key = "#user.username") public UserDO updateUser(UserDO user) { return userRepository.save(user); } // 清除缓存 @CacheEvict(value = "users", key = "#username") public void deleteUser(String username) { userRepository.findByUsername(username) .ifPresent(u -> userRepository.delete(u)); } }
| 属性 | 说明 |
|---|---|
| value | 缓存名称(多个缓存用逗号分隔) |
| key | 缓存键,SpEL 表达式,默认方法参数 |
| condition | 条件缓存,如 condition = "#id > 0" |
| unless | 否定条件,如 unless = "#result == null" |
四、2 spring-boot-redis — Redis 集成
模块定位:使用 Spring Data Redis 操作 Redis 数据库,支持字符串、哈希、列表、集合、有序集合五种数据结构,以及分布式锁功能。
文件结构
核心代码:RedisTemplate 配置
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); // Key 使用 String 序列化 template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); // Value 使用 JSON 序列化(Jackson) Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class); template.setValueSerializer(serializer); template.setHashValueSerializer(serializer); template.afterPropertiesSet(); return template; } }
StringRedisSerializer 或 Jackson2JsonRedisSerializer。
核心代码:完整 Redis 操作封装
@Service public class RedisOptService { @Autowired private RedisTemplate<String, Object> redisTemplate; // === String 类型操作 === public void set(String key, Object value, long seconds) { redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS); } // === Hash 类型操作 === public void hashSet(String key, String field, Object value) { redisTemplate.opsForHash().put(key, field, value); } // === List 类型操作 === public void listPush(String key, Object value) { redisTemplate.opsForList().rightPush(key, value); } // === Set 类型操作 === public void setAdd(String key, Object... values) { redisTemplate.opsForSet().add(key, values); } // === ZSet 类型操作(有序集合)=== public void zSetAdd(String key, Object value, double score) { redisTemplate.opsForZSet().add(key, value, score); } }
核心代码:分布式锁
@Service public class RedisLockService { @Autowired private RedisLockRegistry redisLockRegistry; // 获取分布式锁 public void lock(String lockKey) { Lock lock = redisLockRegistry.obtain(lockKey); lock.lock(); } // 释放锁 public void unlock(String lockKey) { Lock lock = redisLockRegistry.obtain(lockKey); lock.unlock(); } // 尝试获取锁(带超时) public boolean tryLock(String lockKey, long waitTime, long leaseTime) { Lock lock = redisLockRegistry.obtain(lockKey); try { return lock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { return false; } } }
| 配置项 | 说明 |
|---|---|
| spring.data.redis.host | Redis 服务器地址 |
| spring.data.redis.port | 端口(默认 6379) |
| spring.data.redis.password | 密码(如有) |
| spring.data.redis.timeout | 连接超时时间(ms) |
| spring.data.redis.lettuce.pool.max-active | 连接池最大连接数 |
四、3 spring-boot-session — 分布式会话
模块定位:使用 Spring Session 将 Session 存储到 Redis,实现多实例/集群环境下的会话共享。
核心代码:Session 认证拦截器
// LoginInterceptor.java — Session 认证拦截器 public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); UserDO user = (UserDO) session.getAttribute("user"); if (user == null) { response.setStatus(401); return false; // 未登录,拒绝访问 } return true; // 已登录,放行 } }
核心代码:注册拦截器
@Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private LoginInterceptor loginInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns("/api/**") // 拦截所有 API .excludePathPatterns("/api/login"); // 排除登录接口 } }
配置要点
spring: data: redis: host: localhost port: 6379 session: store-type: redis # 指定 Session 存储类型为 Redis timeout: 30m # Session 过期时间
SessionRepositoryFilter)拦截所有请求,将原始的 HttpSession 替换为基于 Redis 的会话实现。多实例部署时,所有实例共享同一个 Redis 中的 Session 数据。
五、消息与邮件 — 消息队列
五、1 spring-boot-activemq — ActiveMQ (JMS)
模块定位:使用 Apache ActiveMQ 实现消息队列,基于 JMS (Java Message Service) API。
核心代码:消息监听器
// MsgController.java — 消息接收 @RestController public class MsgController { // 监听队列消息 @JmsListener(destination = "test.queue") public void receiveQueue(String message) { System.out.println("收到队列消息: " + message); } // 监听主题(发布/订阅) @JmsListener(destination = "test.topic") public void receiveTopic(String message) { System.out.println("收到主题消息: " + message); } }
核心代码:消息发送
@Service public class MsgService { @Autowired private JmsTemplate jmsTemplate; // 发送消息到队列 public void sendQueue(String queueName, String message) { jmsTemplate.convertAndSend(queueName, message); } // 发送消息到主题 public void sendTopic(String topicName, String message) { jmsTemplate.convertAndSend(topicName, message); } }
配置要点
spring: jms: url: tcp://localhost:61616 username: admin password: admin activemq: in-memory: true # 内存模式(测试用)
- 队列(Queue):点对点,一个消息只被一个消费者处理
- 主题(Topic):发布/订阅,一个消息被所有订阅者接收
五、2 spring-boot-rabbitmq — RabbitMQ (AMQP)
模块定位:使用 RabbitMQ 消息中间件,基于 AMQP 协议,支持直接交换、主题交换、扇出交换等多种路由模式。
核心代码:RabbitMQ 配置
@Configuration public class RabbitMQConfig { public static final String QUEUE = "test.queue"; public static final String EXCHANGE = "test.exchange"; @Bean public Queue queue() { return new Queue(QUEUE, true); // durable=true 持久化 } @Bean public DirectExchange exchange() { return new DirectExchange(EXCHANGE, true, false); } @Bean public Binding binding(Queue queue, DirectExchange exchange) { return BindingBuilder.bind(queue).to(exchange).with("test.routingKey"); } }
核心代码:消息发送与接收
// 消息发送 @Service public class MsgService { @Autowired private RabbitTemplate rabbitTemplate; public void send(String message) { rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE, "test.routingKey", message); } } // 消息接收 @RabbitListener(queues = RabbitMQConfig.QUEUE) public void receive(String message) { System.out.println("收到消息: " + message); }
配置要点
spring: rabbitmq: host: localhost port: 5672 username: guest password: guest listener: simple: concurrency: 3 # 最小消费者数 max-concurrency: 10 # 最大消费者数 prefetch: 1 # 每次只处理一条消息 acknowledge-mode: manual # 手动确认
五、3 spring-boot-kafka — Kafka 集成
模块定位:使用 Apache Kafka 分布式消息平台,适用于高吞吐、大数据量场景。
核心代码:Kafka 配置
@Configuration public class KafkaConfig { // 定义 Topic(应用启动时自动创建) @Bean public NewTopic topic() { return new NewTopic("test-topic", 3, (short) 1); // 参数:topic名, 分区数, 副本因子 } }
核心代码:消息发送与接收
// 消息发送 @Service public class KafkaService { @Autowired private KafkaTemplate<String, String> kafkaTemplate; public void send(String topic, String message) { kafkaTemplate.send(topic, message); } } // 消息接收 @Service public class KafkaListenerService { @KafkaListener(topics = "test-topic", groupId = "test-group") public void listen(String message) { System.out.println("收到 Kafka 消息: " + message); } }
配置要点
spring: kafka: bootstrap-servers: localhost:9092 consumer: group-id: test-group auto-offset-restore: earliest # 从最早的消息开始消费 enable-auto-commit: false # 关闭自动提交
| 特性 | Kafka | RabbitMQ |
|---|---|---|
| 消息保留 | 可配置保留(默认 7 天) | 消费后删除 |
| 吞吐量 | 极高(万级/秒) | 高(千级/秒) |
| 延迟 | 毫秒级 | 微秒级 |
| 消息路由 | 简单(按 topic) | 灵活(Exchange 多种模式) |
| 适用场景 | 日志收集、大数据流处理 | 业务系统解耦、异步处理 |
五、4 spring-boot-mail — Java Mail
模块定位:使用 Spring Mail 发送邮件,支持纯文本和 HTML 格式邮件。
核心代码:邮件控制器
@RestController @RequestMapping("/api/email") public class EmailController { @Autowired private JavaMailSender mailSender; @Autowired private MailProperties mailProperties; // 发送简单文本邮件 @PostMapping("/simple") public void sendSimple(String to, String subject, String text) { SimpleMailMessage message = new SimpleMailMessage(); message.setFrom(mailProperties.getFrom()); message.setTo(to); message.setSubject(subject); message.setText(text); mailSender.send(message); } // 发送 HTML 邮件(带附件) @PostMapping("/html") public void sendHtml(MultipartFile attachment) throws MessagingException { MimeMessage message = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message, true); helper.setFrom(mailProperties.getFrom()); helper.setTo("user@example.com"); helper.setSubject("测试邮件"); helper.setText(<h1>Hello</h1><p>这是一封 HTML 邮件</p>, true); // 添加附件 if (attachment != null) { helper.addAttachment(attachment.getOriginalFilename(), attachment.getResource()); } mailSender.send(message); } }
配置要点
spring: mail: host: smtp.gmail.com port: 587 username: your-email@gmail.com password: your-app-password # 应用专用密码(非邮箱密码) properties: mail.smtp.auth: true mail.smtp.starttls.enable: true mail.smtp.starttls.required: true
六、安全与监控 — Actuator
六、1 spring-boot-actuator — 应用监控
模块定位:使用 Spring Boot Actuator 提供生产级应用监控端点,支持健康检查、指标收集、自定义端点等功能。
核心代码:自定义监控端点
@Configuration public class MetricsConfig { @Bean public MeterRegistryCustomizer<Counter> customCounter() { return counter -> counter.tag("app", "my-application"); } } // 自定义端点 @WebEndpoint(id = "custom") public class TestEndpoint { @ReadOperation // GET 请求 public Map<String, Object> info() { Map<String, Object>> result = new HashMap<>(); result.put("version", "1.0.0"); result.put("status", "running"); return result; } }
配置要点
management: endpoints: web: exposure: include: health,info,metrics,custom # 暴露的端点 endpoint: health: show-details: always # 显示详细健康信息
| 端点 | 路径 | 功能 |
|---|---|---|
health | /actuator/health | 应用健康状态(数据库、磁盘等) |
info | /actuator/info | 应用信息(git 版本、构建时间) |
metrics | /actuator/metrics | 各类指标(JVM、CPU、内存) |
env | /actuator/env | 环境变量和配置属性 |
beans | /actuator/beans | 所有 Spring Bean 列表 |
threaddump | /actuator/threaddump | 线程转储信息 |
安全配置(Spring Security 集成)
// SecurityConfig.java — Actuator 安全配置 @Configuration public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(authz -> authz .requestMatchers("/actuator/**").hasRole("ADMIN") # Actuator 需要 ADMIN 角色 .anyRequest().authenticated() ) .httpBasic(Customizer.withDefaults()); return http.build(); } }
management.endpoints.web.exposure.include 精确控制,并结合 Spring Security 限制访问权限。
六、2 spring-boot-aop — AOP 面向切面编程
模块定位:使用 Spring AOP 实现横切关注点(日志、事务、权限校验等)与业务逻辑分离。
核心代码:日志切面
@Aspect @Component public class CalcAspect { // 前置通知:方法执行前 @Before("execution(* cn.javastack.springboot.aop.service.*.*(..))") public void before(JoinPoint joinPoint) { System.out.println("方法: " + joinPoint.getSignature().getName() + " 开始"); } // 后置通知:方法执行后(无论是否异常) @After("execution(* cn.javastack.springboot.aop.service.*.*(..))") public void after(JoinPoint joinPoint) { System.out.println("方法: " + joinPoint.getSignature().getName() + " 结束"); } // 返回后通知:获取返回值 @AfterReturning(value = "execution(* cn.javastack.springboot.aop.service.*.*(..))", returning = "result") public void afterReturning(JoinPoint joinPoint, Object result) { System.out.println("方法: " + joinPoint.getSignature().getName() + " 返回值: " + result); } // 环绕通知:最强大的通知类型 @Around("execution(* cn.javastack.springboot.aop.service.*.*(..))") public Object around(ProceedingJoinPoint pjp) throws Throwable { long start = System.currentTimeMillis(); Object result = pjp.proceed(); // 执行目标方法 long elapsed = System.currentTimeMillis() - start; System.out.println("方法: " + pjp.getSignature().getName() + " 耗时: " + elapsed + "ms"); return result; } // 异常通知:方法抛出异常时 @AfterThrowing(value = "execution(* cn.javastack.springboot.aop.service.*.*(..))", throwing = "e") public void afterThrowing(JoinPoint joinPoint, Exception e) { System.out.println("方法: " + joinPoint.getSignature().getName() + " 异常: " + e.getMessage()); } }
| 注解 | 执行时机 | 能否修改返回值 |
|---|---|---|
@Before | 目标方法执行前 | 否 |
@After | 目标方法执行后(finally) | 否 |
@AfterReturning | 目标方法成功返回后 | 否(但可获取返回值) |
@AfterThrowing | 目标方法抛出异常后 | 否 |
@Around | 包裹目标方法(最强大) | 是(可替换返回值) |
六、3 spring-boot-admin-server — 监控服务端
六、4 spring-boot-admin-client — 监控客户端
模块定位:Spring Boot Admin 是基于 Actuator 的可视化监控平台,提供 UI 界面展示应用健康状态、日志级别、JVM 指标等。
服务端核心代码
@SpringBootApplication @EnableAdminServer // 关键注解:启用 Admin Server public class Application { } // application.yml — 服务端配置 server: port: 9090 spring.boot.admin: ui: title: "Spring Boot Admin"
客户端核心代码
// 客户端 application.yml
spring.boot.admin.client:
url: http://localhost:9090 # Admin Server 地址
instance:
metadata:
user.name: "${spring.security.user.name}"
user.password: "${spring.security.user.password}"
- 应用列表与状态卡片(在线/离线)
- 实时 JVM 内存、CPU、线程监控图表
- 日志级别动态调整(无需重启)
- Bean 列表、环境变量查看
- Actuator 端点直接访问
- 事件通知(应用上下线)
六、5 spring-boot-jasypt — 配置加密
模块定位:使用 Jasypt 对配置文件中的敏感信息(数据库密码、API Key 等)进行加密存储。
配置示例
// application.yml — 加密后的配置
spring:
datasource:
password: ENC(aB3xK9mP2vR7wQ5tL8nY4jF6hD1gS0cE) # 加密密码
使用方式:
- 添加依赖:
com.github.ulisesbocchio:jasypt-spring-boot-starter - 使用
DefaultTextEncryptor生成加密值:
// JasyptTest.java — 生成加密密码 @Test public void encrypt() { DefaultTextEncryptor encryptor = new DefaultTextEncryptor(); encryptor.setPassword("myMasterPassword"); # 加密密钥 String encrypted = encryptor.encrypt("db_password_123"); System.out.println(encrypted); # 输出: aB3xK9mP2vR7wQ5tL8nY4jF6hD1gS0cE }
encryptor.setPassword())不应硬编码在代码中,应通过环境变量或启动参数传入:
# 启动时指定 java -Djasypt.encryptor.password="myMasterPassword" -jar app.jar
七、日志 — Logback
七、1 spring-boot-logging — 默认日志框架
模块定位:Spring Boot 默认使用 Logback 作为日志框架,通过 logback-spring.xml 或 application.yml 配置日志行为。
配置要点:application.yml
logging: level: root: INFO # 根日志级别 cn.javastack: DEBUG # 项目包下 DEBUG 级别 org.springframework: WARN # Spring 框架 WARN 级别 pattern: console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" file: name: logs/application.log # 日志文件路径 max-size: 10MB # 单个日志文件最大大小 max-history: 30 # 保留 30 天日志
FATAL > ERROR > WARN > INFO > DEBUG > TRACE
生产环境推荐 INFO,开发环境推荐 DEBUG。
七、2 spring-boot-tinylog — 轻量级日志框架
模块定位:TinyLog 是一个轻量级 Java 日志框架,配置简单、性能优秀,适合对启动速度敏感的场景。
配置文件:tinylog.properties
# 日志级别 level = INFO # 控制台输出 writer1 = console writer1.format = {date: HH:mm:ss.SSS} {class}:{method} - {message} # 文件输出(滚动策略) writer2 = rolling-file writer2.file = logs/app-{date: yyyy-MM-dd}.log writer2.max-files = 30 # 最多保留 30 个文件 writer2.size = 10 MB # 单个文件最大 10MB writer2.format = {date: HH:mm:ss.SSS} {level}: {class}:{method} - {message}
| 特性 | Logback | TinyLog |
|---|---|---|
| 依赖大小 | ~600KB | ~50KB |
| 启动速度 | 标准 | 更快(配置简单) |
| 功能丰富度 | 丰富(过滤器、异步 Appender) | 基础 |
| 配置方式 | XML / YAML | properties 文件 |
| 适用场景 | 大多数项目(默认) | 轻量级/嵌入式场景 |
八、高级特性 — 定时任务与打包
八、1 spring-boot-schedule — 定时任务
模块定位:使用 Spring @Scheduled 实现定时任务调度,支持 Cron 表达式、固定间隔、固定延迟等模式。
核心代码:简单任务
// 启用定时任务 @SpringBootApplication @EnableScheduling // 关键注解 public class Application { } // SimpleTask.java — 简单定时任务 @Component public class SimpleTask { // 固定间隔执行(每隔 5 秒) @Scheduled(fixedRate = 5000) public void fixedRateTask() { System.out.println("fixedRate: " + LocalDateTime.now()); } // 固定延迟执行(上次完成后等待 5 秒再开始) @Scheduled(fixedDelay = 5000) public void fixedDelayTask() { System.out.println("fixedDelay: " + LocalDateTime.now()); } // Cron 表达式(复杂调度) @Scheduled(cron = "0 0 12 * * ?") # 每天中午 12:00 public void cronTask() { System.out.println("cron: " + LocalDateTime.now()); } }
核心代码:自定义调度器
// TaskConfig.java — 自定义线程池调度器 @Configuration public class TaskConfig implements SchedulingConfigurer { @Override public void configureTasks(TaskScheduler scheduler) { scheduler.scheduleAtFixedRate(() -> { System.out.println("定时任务: " + LocalDateTime.now()); }, 0, 5000); } }
秒 分 时 日 月 周
常见示例:0 0/5 * * * ?(每 5 分钟)、0 30 12 * * ?(每天 12:30)、0 0 0 1 * ?(每月 1 号零点)。
八、2 spring-boot-quartz — Quartz 调度器
模块定位:使用 Quartz 企业级任务调度框架,支持持久化、集群、动态增删改任务等功能。
核心代码:Quartz 配置
// 自定义 Job(继承 QuartzJobBean) public class SimpleTask extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { System.out.println("Quartz 任务执行: " + LocalDateTime.now()); } } // TaskConfig.java — Quartz 配置 @Configuration public class TaskConfig { @Bean public SchedulerFactoryBean schedulerFactoryBean() { try { // 创建 JobDetail JobDetail job = JobBuilder.newJob(SimpleTask.class) .withIdentity("myJob", "myGroup") .storeDurably() .build(); // 创建 Trigger(Cron 触发器) Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("myTrigger", "myGroup") .withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?")) # 每 5 分钟 .build(); SchedulerFactoryBean factory = new SchedulerFactoryBean(); factory.setJobDetails(job); factory.setTriggers(trigger); return factory; } catch (Exception e) { throw new RuntimeException(e); } } }
| 特性 | @Scheduled | Quartz |
|---|---|---|
| 配置复杂度 | 简单(注解) | 复杂(JobDetail + Trigger) |
| 持久化 | 不支持 | 支持(数据库存储) |
| 集群支持 | 不支持 | 支持 |
| 动态管理 | 不支持 | 支持(运行时增删改) |
| 适用场景 | 简单定时任务 | 企业级复杂调度 |
八、3 spring-boot-graalvm — GraalVM 原生镜像
模块定位:使用 GraalVM 将 Spring Boot 应用编译为原生可执行文件,大幅减少启动时间和内存占用。
构建命令
# 使用 native-maven-plugin 构建 mvn package -Pnative -Dnative.image.skipBuildImage=false # 或使用 Docker 构建 docker build -f src/main/docker/Dockerfile.native .
- 构建时间较长(数分钟到数十分钟)
- 部分反射、动态代理需要额外配置(
reflect.json) - 不支持热重载(DevTools 不可用)
- 原生镜像中
-Xmx等 JVM 参数无效(使用--max-heap)
八、4 spring-boot-war — WAR 打包
模块定位:将 Spring Boot 应用打包为 WAR 文件,部署到外部 Tomcat/Jetty 等 Servlet 容器。
核心代码
// pom.xml — 修改打包方式 <packaging>war</packaging> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> # 关键:provided 作用域 </dependency> // Application.java — 继承 SpringBootServletInitializer @SpringBootApplication public class Application extends SpringBootServletInitializer { @Override protected SpringBootServletInitializer configure(SpringApplicationBuilder builder) { return builder.sources(Application.class).build(); } public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
- pom.xml 中修改
<packaging>war</packaging> - Tomcat 依赖设置为
<scope>provided</scope> - Application 类继承
SpringBootServletInitializer - 重写
configure()方法指定启动类
八、5 javastack-spring-boot-starter — 自定义 Starter
模块定位:学习如何编写 Spring Boot 自动配置的 Starter,实现可复用的组件封装。
文件结构
核心代码:配置属性类
@ConfigurationProperties(prefix = "javastack.test") @Data public class TestServiceProperties { private String name = "default"; private Integer timeout = 5000; private Boolean enabled = true; }
核心代码:自动配置类
@Configuration @ConditionalOnProperties(prefix = "javastack.test", name = "enabled", havingValue = "true") @EnableConfigurationProperties(TestServiceProperties.class) // 启用配置属性绑定 public class TestServiceAutoConfiguration { @Autowired private TestServiceProperties properties; @Bean public TestService testService() { return new TestService(properties.getName(), properties.getTimeout()); } }
自动注册(Spring Boot 2.x)
# META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.javastack.springboot.starter.config.TestServiceAutoConfiguration
Spring Boot 3.x 变更: 改用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件:
cn.javastack.springboot.starter.config.TestServiceAutoConfiguration
- 定义配置属性类(
@ConfigurationProperties) - 定义自动配置类(
@Configuration+@Bean) - 使用
@ConditionalOn*条件注解控制装配 - 在
spring.factories中注册自动配置类 - 发布到 Maven 仓库,其他项目通过
spring-boot-starter-xxx依赖
九、测试 — Spring Boot 测试
九、1 spring-boot-test — 完整测试方案
模块定位:Spring Boot 提供完整的测试支持,包括 MockMvc(Web 层)、@MockBean(依赖模拟)、JsonTest(JSON 序列化)等。
文件结构
核心代码:MockMvc 测试
// 启用 Spring Boot 测试环境 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc // 自动配置 MockMvc public class MockMvcTests { @Autowired private MockMvc mockMvc; @Test public void testGetUser() throws Exception { mockMvc.perform(get("/api/users/1")) .andExpect(status().isOk()) .andExpect(jsonPath("$.username").value("testuser")); } @Test public void testCreateUser() throws Exception { mockMvc.perform(post("/api/users") .contentType(MediaType.APPLICATION_JSON) .content("{\"username\":\"newuser\"}")) .andExpect(status().isCreated()) .andExpect(jsonPath("$.id").exists()); } }
核心代码:@MockBean 测试
@SpringBootTest public class MockBeanTests { @MockBean // 模拟 UserRepository 依赖 private UserRepository userRepository; @Autowired private UserController userController; @Test public void testGetUser() { // 模拟依赖返回 BDDMockito.given(userRepository.findById(1L)) .willReturn(Optional.of(new UserDO(1L, "test"))); // 调用控制器 UserDO user = userController.getUser(1L); assertThat(user.getUsername()).isEqualTo("test"); // 验证交互 BDDMockito.then(userRepository).verify().findById(1L); } }
核心代码:JSON 序列化测试
@JsonTest // 仅加载 Jackson 相关配置 public class JsonTests { @Autowired private JacksonTester<UserDO> jsonTester; @Test public void testSerialize() throws IOException { UserDO user = new UserDO(1L, "test", "test@example.com"); // 断言 JSON 序列化结果 assertThat(jsonTester.write(user).getJson()) .hasPathField("id", 1) .hasPathField("username", "test"); } }
核心代码:TestRestTemplate 测试
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class MvcTests { @Autowired private TestRestTemplate testRestTemplate; @Test public void getUserTest() { Result result = testRestTemplate.getForObject( "/user/get?username={username}", Result.class, Map.of("username", "Java技术栈")); assertThat(result.getCode()).isEqualTo(0); assertThat(result.getMsg()).isEqualTo("ok"); } }
| 注解 | 用途 |
|---|---|
@SpringBootTest | 加载完整 Spring 上下文,支持多种 Web 环境 |
@WebMvcTest | 仅加载 Web 层(Controller),不加载 Service/Repository |
@DataJpaTest | 仅加载 JPA 相关组件 |
@JsonTest | 仅加载 Jackson 序列化配置 |
@MockBean | 模拟 Spring Bean(返回 null 或指定值) |
@AutoConfigureMockMvc | 自动配置 MockMvc 用于 Web 层测试 |
@TestPropertySource | 指定测试用属性文件 |
application-test.yml 为测试环境单独配置:
spring: datasource: url: jdbc:h2:mem:testdb # 测试用内存数据库 username: sa password: jpa: hibernate: ddl-auto: create-drop # 测试结束后自动删除
使用 H2 内存数据库,无需安装外部数据库即可完成测试。