Spring Boot 最佳实践

完整学习指南 — 37 个模块全覆盖

基于 Spring Boot 2.7.18 · Java 1.8 · 持续更新中

目录

本指南按照学习路径将 37 个子模块分为 9 个阶段,从入门到高级特性逐一讲解。每个模块包含:原理说明核心代码配置要点注意事项

阶段主题模块数关键技能
一、入门快速开始、项目结构3Hello World、自动装配原理
二、Web 开发RESTful、响应式、服务器4Controller、Filter、API 文档
三、数据持久化SQL/NoSQL/迁移7JPA、MyBatis、MongoDB、ES
四、缓存与会话Redis/缓存抽象3缓存、分布式 Session
五、消息与邮件MQ/Email4Kafka、RabbitMQ、ActiveMQ
六、安全与监控Actuator/Security/Admin5监控、加密、AOP
七、日志Logback/TinyLog2日志配置与替换
八、高级特性定时/GraalVM/WAR/Starter5@Scheduled、Native、自定义 Starter
九、测试集成测试1MockMvc、@SpringBootTest

一、入门 — 快速开始

1.1 spring-boot-quick-start

模块定位:这是整个项目的入口示例,展示了一个最简 Spring Boot 应用的结构。它不依赖任何额外的 starter,只继承父项目的核心依赖(spring-boot-starter-web、lombok、devtools),是理解 Spring Boot "约定优于配置"理念的最佳起点。

文件结构

spring-boot-quick-start/ ├── pom.xml ← 继承父项目,无额外依赖 └── src/main/java/cn/javastack/springboot/quckstart/ └── Application.java ← 唯一源码文件

核心代码

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>
✅ 最佳实践: devtools 设置为 <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(应用监听器)和设计模式的应用。

文件结构

spring-boot-features/ ├── src/main/java/cn/javastack/springboot/features/ │ ├── Application.java ← 启动类 │ ├── core/ │ │ ├── LogService.java ← 日志服务示例 │ │ └── UserService.java ← 用户服务示例 │ ├── listener/ │ │ └── JavastackListener.java ← 应用事件监听器 │ └── analyzer/ │ ├── JavastackException.java ← 自定义异常 │ ├── JavastackFailureAnalyzer.java ← 自定义失败分析器 │ └── PortInUseFailureAnalyzer.java ← 端口占用分析器 └── src/main/resources/application.yml

核心代码: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;
    }
}
✅ 最佳实践: 在 Spring Boot 中,@Service 注解本身不包含任何特殊行为,它只是一个语义标记。Spring 会自动将标注了 @Service 的类注册为 Bean。

1.3 spring-boot-application — 应用入口与设计模式

模块定位:展示 Spring Boot 应用入口的多种写法,以及观察者设计模式在 Spring 事件机制中的应用。这是一个"空壳"模块,主要代码量不大,但概念重要。

文件结构

spring-boot-application/ ├── src/main/java/cn/javastack/application/ │ ├── Application.java ← 启动类(多种启动方式示例) │ └── designpattern/observer/ │ ├── JavaStackEvent.java ← 自定义事件 │ ├── ObserverConfiguration.java ← 监听器配置 │ └── ReaderListener.java ← 事件监听器

核心代码:观察者模式

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 客户端调用、安全配置等。作为学习材料,它的价值非常高。

文件结构

spring-boot-web/ ├── src/main/java/cn/javastack/springboot/web/ │ ├── Application.java ← 启动类 │ ├── bean/ │ │ ├── OrderInfo.java ← 订单 VO │ │ ├── User.java ← 用户 VO │ │ └── UserXml.java ← XML 序列化 VO │ ├── config/ │ │ ├── CustomTomcatWebServerFactoryCustomizer ← 自定义 Tomcat 配置 │ │ ├── SecurityConfig.java ← Spring Security 配置 │ │ ├── ServerConfig.java ← 服务器配置 │ │ └── WebConfig.java ← Web 配置(拦截器、转换器等) │ ├── controller/ │ │ ├── CallRestController.java ← REST 客户端调用示例 │ │ ├── ErrorPageController.java ← 自定义错误页面 │ │ ├── LoginController.java ← 登录接口 │ │ └── ResponseBodyController.java ← @ResponseBody 示例 │ ├── handler/ │ │ ├── CustomConverter.java ← String → Object 转换器 │ │ ├── CustomRestTemplateCustomizer.java ← RestTemplate 定制器 │ │ ├── ErrorRegister.java ← 错误页面注册 │ │ └── GlobalExceptionHandler.java ← 全局异常处理 │ └── servlet/ │ ├── InitServlet.java ← 自定义 Servlet │ ├── JavaFilter.java ← 自定义 Filter │ └── RegisterServlet.java ← Servlet 注册 ├── src/main/resources/ │ ├── application.yml │ └── i18n/ ← 国际化资源文件 │ ├── common.properties │ ├── index.properties │ └── index_zh_CN.properties

核心代码: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);
    }
}
💡 @ControllerAdvice 的作用范围:

核心代码: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)

资源文件组织:

resources/i18n/ ├── common.properties ← 默认(英文) ├── index.properties ← 默认(英文) └── index_zh_CN.properties ← 中文
// 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);
    }
}
⚠️ Spring Security 6.x 变更: Spring Boot 3.x 内置的 Spring Security 6.x 中,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
✅ 最佳实践:

2.2 spring-boot-webflux — 响应式 Web

模块定位:展示 Spring WebFlux 响应式编程模型,基于 Netty 服务器,非阻塞 I/O,适合高并发场景。

文件结构

spring-boot-webflux/ ├── src/main/java/cn/javastack/springboot/webflux/ │ ├── Application.java │ ├── bean/User.java │ ├── config/WebConfig.java │ └── controller/CallRestController.java └── src/main/resources/application.yml

核心代码: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 MVCWebFlux
运行时Servlet 容器(Tomcat/Jetty)Netty(内置)
模型阻塞式线程池非响应式流(Reactor/RxJava)
返回类型UserMono<User> / Flux<User>
适用场景传统 CRUD、事务操作高并发、流式处理、WebSocket
学习曲线高(需理解响应式编程)
💡 Mono vs Flux:

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  # 是否禁用缓冲
✅ 何时选择 Undertow? 何时继续用 Tomcat?

2.4 spring-boot-knife4j — API 文档

模块定位:集成 Knife4j(基于 Swagger/OpenAPI),自动生成接口文档,支持在线调试。

文件结构

spring-boot-knife4j/ ├── src/main/java/cn/javastack/springboot/knife4j/ │ ├── Knife4jApplication.java │ ├── api/Knife4jController.java ← 接口定义 │ └── config/Knife4jConfiguration.java ← Knife4j 配置 └── src/main/resources/application.yml

核心代码:接口定义

@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 文档界面。

⚠️ Knife4j 版本注意: Knife4j 2.x 基于 Swagger 2,3.x 已迁移到 OpenAPI 3。Spring Boot 3.x 建议使用 Knife4j 4.x(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 统计等高级功能。

文件结构

spring-boot-datasource/ ├── src/main/java/cn/javastack/springboot/ds/ │ ├── Application.java │ ├── DsConfig.java ← Druid 配置类 │ ├── UserDao.java ← DAO 接口 │ └── dao/impl/UserDaoImpl.java ← DAO 实现 └── src/main/resources/application.yml

核心代码: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
✅ Druid 关键参数说明:
参数含义推荐值
initial-size初始化连接数5(中小型应用)
max-active最大连接数根据并发量调整,通常 20-100
max-wait获取连接最大等待时间(ms)60000(60秒)
test-while-idle空闲时检测连接有效性true(必须开启)
pool-prepared-statements开启 PSCachetrue(Oracle/MySQL 5.7+)
💡 Druid 监控台: 访问 http://localhost:8080/druid 可以看到:

3.2 spring-boot-jpa — Spring Data JPA

模块定位:使用 Spring Data JPA 进行 ORM 操作,是 Spring Boot 中最流行的 SQL 持久化方案。

文件结构

spring-boot-jpa/ ├── src/main/java/cn/javastack/springboot/jpa/ │ ├── Application.java │ ├── entity/UserDO.java ← 实体类 │ ├── repo/UserRepository.java ← 数据访问接口 │ └── controller/UserController.java ← 控制层 └── src/main/resources/application.yml

核心代码:实体类

@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
⚠️ ddl-auto 参数选择:
行为适用场景
create启动时删除所有表,重新创建纯测试环境
create-drop启动时创建,关闭时删除单元测试
update只更新表结构,不删除已有数据开发环境
validate只校验,不执行任何修改生产环境(推荐)
none不做任何操作使用 Flyway/Liquibase 管理迁移

3.3 spring-boot-mybatis — MyBatis 集成

模块定位:使用 MyBatis 半自动 ORM 框架,适合需要精细控制 SQL 的场景。

文件结构

spring-boot-mybatis/ ├── src/main/java/cn/javastack/springboot/mybatis/ │ ├── Application.java │ ├── entity/UserDO.java │ ├── mapper/UserMapper.java ← Mapper 接口 │ └── controller/UserController.java └── src/main/resources/ ├── application.yml └── mapper/UserMapper.xml ← 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
✅ MyBatis 核心优势:

3.4 spring-boot-mybatis-plus — MyBatis-Plus 增强

模块定位:MyBatis 的增强框架,在保留 MyBatis 灵活性的同时,提供 CRUD 插件、代码生成器、分页插件等开箱即用的功能。

文件结构

spring-boot-mybatis-plus/ ├── src/main/java/cn/javastack/springboot/mybatisplus/ │ ├── Application.java │ ├── config/CustomMetaObjectHandler.java ← 自动填充处理器 │ ├── entity/UserDO.java ← 实体类 │ ├── mapper/UserMapper.java ← Mapper 接口 │ ├── service/UserService.java ← Service 接口 │ ├── service/UserServiceImpl.java ← Service 实现 │ └── controller/UserController.java ← 控制层 └── src/main/resources/application.yml

核心代码:实体类(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-Plus 核心优势:

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);
    }
}
⚠️ Spring Boot 3.x 变更: Spring Boot 3.x 中 ElasticsearchRepository 已被标记为废弃,推荐使用 ElasticsearchClient

3.7 spring-boot-flyway — 数据库版本迁移

模块定位:使用 Flyway 进行数据库版本管理,在应用启动时自动执行 SQL 迁移脚本,确保数据库结构与代码一致。

文件结构

spring-boot-flyway/ ├── src/main/java/cn/javastack/springboot/flyway/ │ └── Application.java ├── src/main/resources/ │ ├── application.yml │ └── db/migration/ ← 迁移脚本目录 │ ├── V1__create_user_table.sql │ └── V2__add_user_fields.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  # 执行前校验
✅ Flyway 核心概念:
  • 文件名格式:V{版本号}__{描述}.sql,如 V1__create_user_table.sql
  • Flyway 在 flyway_schema_history 表中记录已执行的迁移
  • 迁移脚本按文件名排序执行,不可修改已执行的脚本
  • baseline-on-migrate: true:对已有数据库,将其当前状态标记为基线版本
💡 Flyway vs Liquibase 对比:
特性FlywayLiquibase
脚本格式纯 SQLXML/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));
    }
}
✅ @Cacheable 注解参数:
属性说明
value缓存名称(多个缓存用逗号分隔)
key缓存键,SpEL 表达式,默认方法参数
condition条件缓存,如 condition = "#id > 0"
unless否定条件,如 unless = "#result == null"

四、2 spring-boot-redis — Redis 集成

模块定位:使用 Spring Data Redis 操作 Redis 数据库,支持字符串、哈希、列表、集合、有序集合五种数据结构,以及分布式锁功能。

文件结构

spring-boot-redis/ ├── src/main/java/cn/javastack/springboot/redis/ │ ├── Application.java │ ├── config/RedisConfig.java ← RedisTemplate 配置 │ ├── controller/RedisController.java ← 基础操作接口 │ └── service/ │ ├── RedisLockService.java ← 分布式锁 │ └── RedisOptService.java ← 完整 CRUD 封装 └── src/main/resources/application.yml

核心代码: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;
    }
}
⚠️ 序列化问题: 如果不配置序列化器,默认使用 JDK 序列化,会导致 Key/Value 在 Redis 中显示为二进制乱码。必须显式配置 StringRedisSerializerJackson2JsonRedisSerializer

核心代码:完整 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;
        }
    }
}
✅ Redis 配置要点:
配置项说明
spring.data.redis.hostRedis 服务器地址
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 过期时间
✅ 分布式 Session 原理: Spring Session 通过一个 Filter(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    # 内存模式(测试用)
💡 JMS 两种消息模式:
  • 队列(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 vs RabbitMQ 对比:
特性KafkaRabbitMQ
消息保留可配置保留(默认 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
⚠️ 安全提醒: Gmail 等主流邮箱已禁用"允许不够安全的应用",必须使用应用专用密码(Google 账户 → 安全 → 两步验证 → 应用专用密码)。

六、安全与监控 — 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    # 显示详细健康信息
✅ Actuator 内置端点:
端点路径功能
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();
    }
}
⚠️ 安全警告: 生产环境绝不能暴露所有 Actuator 端点!必须通过 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());
    }
}
✅ AOP 通知类型对比:
注解执行时机能否修改返回值
@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}"
✅ Spring Boot Admin 核心功能:
  • 应用列表与状态卡片(在线/离线)
  • 实时 JVM 内存、CPU、线程监控图表
  • 日志级别动态调整(无需重启)
  • Bean 列表、环境变量查看
  • Actuator 端点直接访问
  • 事件通知(应用上下线)

六、5 spring-boot-jasypt — 配置加密

模块定位:使用 Jasypt 对配置文件中的敏感信息(数据库密码、API Key 等)进行加密存储。

配置示例

// application.yml — 加密后的配置
spring:
  datasource:
    password: ENC(aB3xK9mP2vR7wQ5tL8nY4jF6hD1gS0cE)  # 加密密码

使用方式:

  1. 添加依赖:com.github.ulisesbocchio:jasypt-spring-boot-starter
  2. 使用 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.xmlapplication.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 vs TinyLog 对比:
特性LogbackTinyLog
依赖大小~600KB~50KB
启动速度标准更快(配置简单)
功能丰富度丰富(过滤器、异步 Appender)基础
配置方式XML / YAMLproperties 文件
适用场景大多数项目(默认)轻量级/嵌入式场景

八、高级特性 — 定时任务与打包

八、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);
    }
}
✅ Cron 表达式格式(6 位): 秒 分 时 日 月 周

常见示例: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);
        }
    }
}
✅ Quartz vs @Scheduled 对比:
特性@ScheduledQuartz
配置复杂度简单(注解)复杂(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 .
⚠️ GraalVM 注意事项:
  • 构建时间较长(数分钟到数十分钟)
  • 部分反射、动态代理需要额外配置(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);
    }
}
✅ WAR 打包关键步骤:
  1. pom.xml 中修改 <packaging>war</packaging>
  2. Tomcat 依赖设置为 <scope>provided</scope>
  3. Application 类继承 SpringBootServletInitializer
  4. 重写 configure() 方法指定启动类

八、5 javastack-spring-boot-starter — 自定义 Starter

模块定位:学习如何编写 Spring Boot 自动配置的 Starter,实现可复用的组件封装。

文件结构

javastack-spring-boot-starter/ ├── src/main/java/cn/javastack/springboot/starter/ │ ├── config/ │ │ └── TestServiceAutoConfiguration.java ← 自动配置类 │ └── properties/ │ └── TestServiceProperties.java ← 配置属性类 ├── src/main/resources/META-INF/ │ └── spring.factories ← 自动注册(Spring Boot 2.x) └── pom.xml

核心代码:配置属性类

@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
✅ 自定义 Starter 核心步骤:
  1. 定义配置属性类(@ConfigurationProperties
  2. 定义自动配置类(@Configuration + @Bean
  3. 使用 @ConditionalOn* 条件注解控制装配
  4. spring.factories 中注册自动配置类
  5. 发布到 Maven 仓库,其他项目通过 spring-boot-starter-xxx 依赖

九、测试 — Spring Boot 测试

九、1 spring-boot-test — 完整测试方案

模块定位:Spring Boot 提供完整的测试支持,包括 MockMvc(Web 层)、@MockBean(依赖模拟)、JsonTest(JSON 序列化)等。

文件结构

spring-boot-test/ ├── src/test/java/cn/javastack/springboot/test/ │ ├── testcase/MockMvcTests.java ← Web 层测试 │ ├── testcase/JsonTests.java ← JSON 序列化测试 │ ├── testcase/MockBeanTests.java ← 依赖模拟测试 │ └── testcase/MvcTests.java ← TestRestTemplate 测试 ├── src/test/resources/ │ └── application-test.yml ← 测试环境配置 └── pom.xml

核心代码: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");
    }
}
✅ Spring Boot 测试注解速查:
注解用途
@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 内存数据库,无需安装外部数据库即可完成测试。