跳转至

Spring Boot 自动配置、AOP 与事件机制

本章为 Spring Boot 核心原理与企业级应用扩展讲解,适用于中高级开发者。 内容覆盖自动配置加载流程、AOP 面向切面编程、Spring 事件机制三大核心模块。 并结合前后端分离项目场景,讲解统一日志、权限审计、异步通知等实战案例。


一、Spring Boot 自动配置原理

1️⃣ 自动配置的核心思想

Spring Boot 的「约定优于配置」依赖于自动配置机制(AutoConfiguration)。
通过 @EnableAutoConfiguration 自动导入一系列基于条件判断的配置类,实现组件自动装配。

@SpringBootApplication
public class DemoApplication {
  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }
}

该注解内部包含:@EnableAutoConfiguration@Import(AutoConfigurationImportSelector.class)

AutoConfigurationImportSelector 会从以下位置加载所有自动配置:

META-INF/spring.factories  (Spring Boot 2.x)
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (Spring Boot 3.x)

2️⃣ 加载机制流程图

flowchart TD
A[SpringApplication.run()] --> B[读取 spring.factories]
B --> C[扫描所有 AutoConfiguration 类]
C --> D[匹配 @Conditional 条件注解]
D --> E[注册满足条件的 Bean 到容器]
E --> F[应用启动完成,Bean 可用]

3️⃣ 条件注解 @Conditional 家族

注解 说明
@ConditionalOnClass 指定类存在时生效
@ConditionalOnMissingBean 当容器中不存在指定 Bean 时生效
@ConditionalOnProperty 根据配置文件属性值启用或禁用
@ConditionalOnWebApplication 当前环境为 Web 应用时生效
@ConditionalOnExpression 使用 SpEL 表达式判断

示例:

@Configuration
@ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver")
public class MyDataSourceAutoConfig {

  @Bean
  @ConditionalOnMissingBean
  public DataSource dataSource() {
    return DataSourceBuilder.create()
      .url("jdbc:mysql://localhost:3306/demo")
      .username("root")
      .password("123456")
      .build();
  }
}

4️⃣ 自定义 Starter 案例

spring-boot-starter-hello/
 ├─ src/main/java/com/example/hello/
 │   ├─ HelloAutoConfiguration.java
 │   └─ HelloService.java
 └─ resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

HelloAutoConfiguration.java

@Configuration
@ConditionalOnMissingBean(HelloService.class)
public class HelloAutoConfiguration {
  @Bean
  public HelloService helloService() {
    return new HelloService("Spring Boot Starter Hello!");
  }
}

配置文件 (AutoConfiguration.imports)

com.example.hello.HelloAutoConfiguration

在其他项目中引入依赖后,无需配置即可使用:

@Autowired HelloService hello;
System.out.println(hello.sayHello());

二、AOP(面向切面编程)

1️⃣ AOP 概念与应用场景

AOP(Aspect-Oriented Programming)用于将横切关注点(如日志、事务、安全、性能监控)与业务逻辑解耦。

在前后端分离项目中常用于:

  • 接口访问日志记录
  • 权限校验(基于注解的接口安全控制)
  • 性能统计与异常报警

2️⃣ 核心概念

名称 含义
JoinPoint 程序执行点(方法调用等)
Pointcut 切入点,定义匹配规则
Advice 增强逻辑(Before/After/Around)
Aspect 切面,整合切入点与增强
Weaving 将切面织入目标对象的过程

3️⃣ 常用注解

注解 说明
@Aspect 声明切面类
@Before 方法执行前执行
@After 方法执行后执行
@AfterReturning 方法正常返回后执行
@AfterThrowing 抛出异常后执行
@Around 环绕执行,可控制方法是否继续

4️⃣ 实战案例:接口日志切面

@Aspect
@Component
public class LogAspect {

  @Pointcut("execution(* com.example.demo.controller..*(..))")
  public void logPointcut() {}

  @Around("logPointcut()")
  public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
    long start = System.currentTimeMillis();
    String method = joinPoint.getSignature().toShortString();
    System.out.println("[LOG] Start -> " + method);
    Object result = joinPoint.proceed();
    long end = System.currentTimeMillis();
    System.out.println("[LOG] End -> " + method + " (" + (end - start) + "ms)");
    return result;
  }
}

在前后端分离项目中,可用于记录接口耗时与请求参数。

5️⃣ AOP 与拦截器 / 过滤器 对比

特性 AOP HandlerInterceptor Filter
级别 Spring 层 Spring MVC 层 Servlet 容器层
作用范围 方法调用 Controller 请求 所有 HTTP 请求
典型用途 日志、事务、权限 登录验证、参数处理 编码、跨域、XSS 防护
执行时机 调用方法时 控制器执行前后 请求进入容器时

6️⃣ 执行顺序图

sequenceDiagram
participant Browser
participant Filter
participant Interceptor
participant AOP
participant Controller

Browser->>Filter: 请求进入
Filter->>Interceptor: 通过过滤器链
Interceptor->>AOP: 进入业务切面
AOP->>Controller: 执行业务逻辑
Controller-->>AOP: 返回结果
AOP-->>Interceptor: 后置处理
Interceptor-->>Filter: 响应封装
Filter-->>Browser: 响应返回

三、Spring 事件机制

Spring 提供基于观察者模式的事件发布 / 监听体系。

1️⃣ 基本组成

组件 作用
ApplicationEvent 事件对象
ApplicationEventPublisher 事件发布者
ApplicationListener 事件监听器
@EventListener 注解方式监听事件

2️⃣ 示例:用户注册事件

// 事件定义
public class UserRegisterEvent extends ApplicationEvent {
  private final String username;
  public UserRegisterEvent(Object source, String username) {
    super(source);
    this.username = username;
  }
  public String getUsername() { return username; }
}
// 发布事件
@Service
public class UserService {
  @Autowired private ApplicationEventPublisher publisher;
  public void register(String username) {
    System.out.println("注册用户:" + username);
    publisher.publishEvent(new UserRegisterEvent(this, username));
  }
}
// 监听事件
@Component
public class WelcomeEmailListener {
  @EventListener
  public void onRegister(UserRegisterEvent event) {
    System.out.println("发送欢迎邮件给:" + event.getUsername());
  }
}

3️⃣ 异步事件监听

@EnableAsync
@Component
public class AsyncEventListener {
  @Async
  @EventListener
  public void handle(UserRegisterEvent event) {
    System.out.println("异步处理用户注册:" + event.getUsername());
  }
}

4️⃣ 事务事件监听

@Component
public class TxEventListener {
  @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
  public void afterCommit(UserRegisterEvent e) {
    System.out.println("事务提交后发送通知:" + e.getUsername());
  }
}

5️⃣ 事件流程图

flowchart LR
A[UserService 发布事件] --> B[ApplicationEventPublisher]
B --> C[ApplicationListener 匹配监听器]
C --> D{同步/异步执行}
D --> E[业务处理逻辑执行]

四、综合实战:用户注册日志 + 异步事件通知

@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
  private final UserService userService;
  @PostMapping("/register")
  public String register(@RequestParam String name) {
    userService.register(name);
    return "OK";
  }
}
  • AOP 记录接口日志
  • 事件监听异步发送欢迎邮件
  • 可扩展为消息队列通知(Kafka/RabbitMQ)

五、课堂任务

1️⃣ 编写一个自定义 Starter:实现系统启动时输出公司名称。
2️⃣ 使用 AOP 实现接口性能监控与异常捕获。
3️⃣ 使用 Spring 事件机制在订单支付后发送短信通知。
4️⃣ 思考:如果事件监听器异常,会不会影响主流程?