2528 字
约 8 分钟
1
🎯 SpringBoot AOP
2026-09-19
无标签

🎯 第14章 SpringBoot AOP

掌握面向切面编程,实现横切关注点的优雅处理 💻 查看完整代码 - 在线IDE体验 学习目标

📋 学习目标

  • 理解AOP概念- 面向切面编程思想
  • 掌握核心术语- 切面、切点、通知等
  • 学会配置AOP- 注解与XML配置
  • 实现通知类型- 前置、后置、环绕通知
  • 掌握切点表达式- 精确定位目标方法
  • 应用实战场景- 日志、事务、权限控制

AOP核心概念

🔍 AOP核心概念

  • 切面(Aspect)- 横切关注点的模块化
  • 连接点(JoinPoint)- 程序执行的特定点
  • 切点(Pointcut)- 连接点的集合
  • 通知(Advice)- 切面在特定连接点执行的动作
  • 目标对象(Target)- 被通知的对象
  • 代理(Proxy)- AOP框架创建的对象

AOP依赖配置

📦 AOP依赖配置

<!-- pom.xml AOP依赖配置 -->
<dependencies>
    <!-- SpringBoot AOP启动器 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
    <!-- SpringBoot Web启动器 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- SpringBoot 测试启动器 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

# application.yml AOP配置
spring:
  aop:
    # 启用AOP自动配置
    auto: true
    # 使用CGLIB代理
    proxy-target-class: true
    
# 日志配置
logging:
  level:
    cn.bugstack.springframework.boot.aop: DEBUG
    org.springframework.aop: DEBUG
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"

AOP执行流程

🔄 AOP执行流程

1目标方法调用 客户端调用目标对象的方法 2代理拦截 AOP代理拦截方法调用 3前置通知 执行@Before通知方法 4目标方法执行 调用实际的目标方法 5后置通知 执行@After、@AfterReturning等通知 切面示例

🎯 切面实现示例

// LoggingAspect.java - 日志切面
@Aspect
@Component
@Slf4j
public class LoggingAspect {
    
    /**
     * 定义切点:拦截service包下所有类的所有方法
     */
    @Pointcut("execution(* cn.bugstack.springframework.boot.aop.service.*.*(..))")
    public void servicePointcut() {}
    
    /**
     * 前置通知:方法执行前记录日志
     */
    @Before("servicePointcut()")
    public void beforeAdvice(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        Object[] args = joinPoint.getArgs();
        
        log.info("=== 方法执行前 ===");
        log.info("类名: {}", className);
        log.info("方法名: {}", methodName);
        log.info("参数: {}", Arrays.toString(args));
        log.info("执行时间: {}", LocalDateTime.now());
    }
    
    /**
     * 后置通知:方法执行后记录日志
     */
    @AfterReturning(pointcut = "servicePointcut()", returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        
        log.info("=== 方法执行后 ===");
        log.info("方法名: {}", methodName);
        log.info("返回值: {}", result);
        log.info("执行完成时间: {}", LocalDateTime.now());
    }
    
    /**
     * 异常通知:方法抛出异常时记录日志
     */
    @AfterThrowing(pointcut = "servicePointcut()", throwing = "exception")
    public void afterThrowingAdvice(JoinPoint joinPoint, Exception exception) {
        String methodName = joinPoint.getSignature().getName();
        
        log.error("=== 方法执行异常 ===");
        log.error("方法名: {}", methodName);
        log.error("异常信息: {}", exception.getMessage());
        log.error("异常类型: {}", exception.getClass().getSimpleName());
    }
    
    /**
     * 环绕通知:完全控制方法执行
     */
    @Around("servicePointcut()")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        String methodName = proceedingJoinPoint.getSignature().getName();
        long startTime = System.currentTimeMillis();
        
        log.info("=== 环绕通知开始 ===");
        log.info("方法名: {}", methodName);
        
        try {
            // 执行目标方法
            Object result = proceedingJoinPoint.proceed();
            
            long endTime = System.currentTimeMillis();
            log.info("=== 环绕通知结束 ===");
            log.info("方法名: {}", methodName);
            log.info("执行耗时: {}ms", endTime - startTime);
            
            return result;
        } catch (Exception e) {
            log.error("环绕通知捕获异常: {}", e.getMessage());
            throw e;
        }
    }
}

// UserService.java - 目标服务类
@Service
@Slf4j
public class UserService {
    
    public User getUserById(Long id) {
        log.info("查询用户信息,ID: {}", id);
        
        // 模拟数据库查询
        if (id == null || id <= 0) {
            throw new IllegalArgumentException("用户ID不能为空或小于等于0");
        }
        
        User user = new User();
        user.setId(id);
        user.setUsername("user" + id);
        user.setEmail("user" + id + "@example.com");
        user.setCreateTime(LocalDateTime.now());
        
        return user;
    }
    
    public List<User> getAllUsers() {
        log.info("查询所有用户信息");
        
        List<User> users = new ArrayList<>();
        for (int i = 1; i <= 3; i++) {
            User user = new User();
            user.setId((long) i);
            user.setUsername("user" + i);
            user.setEmail("user" + i + "@example.com");
            user.setCreateTime(LocalDateTime.now());
            users.add(user);
        }
        
        return users;
    }
    
    public User saveUser(User user) {
        log.info("保存用户信息: {}", user);
        
        if (user == null) {
            throw new IllegalArgumentException("用户信息不能为空");
        }
        
        // 模拟保存操作
        user.setId(System.currentTimeMillis());
        user.setCreateTime(LocalDateTime.now());
        
        return user;
    }
}

// User.java - 用户实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Long id;
    private String username;
    private String email;
    private LocalDateTime createTime;
}

AOP切面图示

🎨 AOP切面图示

                @Before前置通知
            →
                Target Method目标方法
            →
                @After后置通知

@Around 环绕通知可以完全控制整个执行流程

@AfterThrowing 异常通知在方法抛出异常时执行

@AfterReturning 返回通知在方法正常返回时执行 切点表达式

📍 切点表达式

  • execution- 匹配方法执行
  • within- 匹配类型内的方法
  • this- 匹配代理对象类型
  • target- 匹配目标对象类型
  • args- 匹配方法参数
  • @annotation- 匹配注解

通知类型

📢 通知类型

  • @Before- 前置通知,方法执行前
  • @After- 后置通知,方法执行后
  • @AfterReturning- 返回通知,正常返回后
  • @AfterThrowing- 异常通知,抛出异常后
  • @Around- 环绕通知,完全控制执行
  • @Pointcut- 切点定义,可重用

性能监控切面

📊 性能监控切面

// PerformanceAspect.java - 性能监控切面
@Aspect
@Component
@Slf4j
public class PerformanceAspect {
    
    /**
     * 自定义注解切点
     */
    @Pointcut("@annotation(cn.bugstack.springframework.boot.aop.annotation.PerformanceMonitor)")
    public void performancePointcut() {}
    
    /**
     * 环绕通知:监控方法执行性能
     */
    @Around("performancePointcut()")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        
        // 记录开始时间
        long startTime = System.currentTimeMillis();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        
        log.info("🚀 开始执行方法: {}.{}", className, methodName);
        
        try {
            // 执行目标方法
            Object result = joinPoint.proceed();
            
            // 记录结束时间
            stopWatch.stop();
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;
            
            // 性能分析
            if (executionTime > 1000) {
                log.warn("⚠️ 慢方法警告: {}.{} 执行耗时: {}ms", className, methodName, executionTime);
            } else if (executionTime > 500) {
                log.info("⏰ 方法执行: {}.{} 执行耗时: {}ms", className, methodName, executionTime);
            } else {
                log.debug("✅ 方法执行: {}.{} 执行耗时: {}ms", className, methodName, executionTime);
            }
            
            // 记录到监控系统(可集成Micrometer)
            recordMetrics(className, methodName, executionTime);
            
            return result;
            
        } catch (Exception e) {
            stopWatch.stop();
            long executionTime = System.currentTimeMillis() - startTime;
            
            log.error("❌ 方法执行异常: {}.{} 执行耗时: {}ms 异常: {}", 
                className, methodName, executionTime, e.getMessage());
            
            // 记录异常指标
            recordErrorMetrics(className, methodName, e);
            
            throw e;
        }
    }
    
    /**
     * 记录性能指标
     */
    private void recordMetrics(String className, String methodName, long executionTime) {
        // 可以集成Micrometer、Prometheus等监控系统
        Timer.Sample sample = Timer.start();
        sample.stop(Timer.builder("method.execution.time")
            .tag("class", className)
            .tag("method", methodName)
            .register(Metrics.globalRegistry));
    }
    
    /**
     * 记录异常指标
     */
    private void recordErrorMetrics(String className, String methodName, Exception e) {
        Counter.builder("method.execution.error")
            .tag("class", className)
            .tag("method", methodName)
            .tag("exception", e.getClass().getSimpleName())
            .register(Metrics.globalRegistry)
            .increment();
    }
}

// PerformanceMonitor.java - 性能监控注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PerformanceMonitor {
    
    /**
     * 监控描述
     */
    String value() default "";
    
    /**
     * 慢方法阈值(毫秒)
     */
    long threshold() default 1000;
}

// 使用示例
@Service
public class OrderService {
    
    @PerformanceMonitor("查询订单信息")
    public Order getOrderById(Long orderId) {
        // 模拟数据库查询
        try {
            Thread.sleep(800); // 模拟耗时操作
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        return new Order(orderId, "ORDER-" + orderId, new BigDecimal("99.99"));
    }
    
    @PerformanceMonitor(value = "创建订单", threshold = 2000)
    public Order createOrder(Order order) {
        // 模拟复杂业务逻辑
        try {
            Thread.sleep(1500); // 模拟耗时操作
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        order.setId(System.currentTimeMillis());
        return order;
    }
}

权限控制切面

🔐 权限控制切面

// SecurityAspect.java - 权限控制切面
@Aspect
@Component
@Slf4j
public class SecurityAspect {
    
    @Autowired
    private UserContext userContext;
    
    /**
     * 权限检查切点
     */
    @Pointcut("@annotation(cn.bugstack.springframework.boot.aop.annotation.RequirePermission)")
    public void securityPointcut() {}
    
    /**
     * 前置通知:权限检查
     */
    @Before("securityPointcut() && @annotation(requirePermission)")
    public void checkPermission(JoinPoint joinPoint, RequirePermission requirePermission) {
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String[] permissions = requirePermission.value();
        
        log.info("🔒 权限检查: {}.{} 需要权限: {}", className, methodName, Arrays.toString(permissions));
        
        // 获取当前用户
        User currentUser = userContext.getCurrentUser();
        if (currentUser == null) {
            log.warn("❌ 权限检查失败: 用户未登录");
            throw new SecurityException("用户未登录,请先登录");
        }
        
        // 检查用户权限
        Set<String> userPermissions = getUserPermissions(currentUser);
        boolean hasPermission = Arrays.stream(permissions)
            .anyMatch(userPermissions::contains);
        
        if (!hasPermission) {
            log.warn("❌ 权限检查失败: 用户 {} 缺少权限 {}", 
                currentUser.getUsername(), Arrays.toString(permissions));
            throw new SecurityException("权限不足,无法访问该资源");
        }
        
        log.info("✅ 权限检查通过: 用户 {} 访问 {}.{}", 
            currentUser.getUsername(), className, methodName);
    }
    
    /**
     * 获取用户权限
     */
    private Set<String> getUserPermissions(User user) {
        // 模拟从数据库或缓存中获取用户权限
        Set<String> permissions = new HashSet<>();
        
        if ("admin".equals(user.getUsername())) {
            permissions.add("user:read");
            permissions.add("user:write");
            permissions.add("user:delete");
            permissions.add("order:read");
            permissions.add("order:write");
        } else if ("manager".equals(user.getUsername())) {
            permissions.add("user:read");
            permissions.add("order:read");
            permissions.add("order:write");
        } else {
            permissions.add("user:read");
            permissions.add("order:read");
        }
        
        return permissions;
    }
}

// RequirePermission.java - 权限注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequirePermission {
    
    /**
     * 需要的权限列表
     */
    String[] value();
    
    /**
     * 权限检查模式:ALL-需要所有权限,ANY-需要任一权限
     */
    PermissionMode mode() default PermissionMode.ANY;
}

// PermissionMode.java - 权限模式枚举
public enum PermissionMode {
    ALL,  // 需要所有权限
    ANY   // 需要任一权限
}

// 使用示例
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @GetMapping("/{id}")
    @RequirePermission("user:read")
    public User getUserById(@PathVariable Long id) {
        return userService.getUserById(id);
    }
    
    @PostMapping
    @RequirePermission("user:write")
    public User createUser(@RequestBody User user) {
        return userService.saveUser(user);
    }
    
    @DeleteMapping("/{id}")
    @RequirePermission(value = {"user:delete", "admin"}, mode = PermissionMode.ANY)
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
}

AOP注解对比表

📊 AOP注解对比

注解类型 执行时机 参数 使用场景
@Before 方法执行前 JoinPoint 参数校验、权限检查
@After 方法执行后(无论成功失败) JoinPoint 资源清理、日志记录
@AfterReturning 方法正常返回后 JoinPoint, 返回值 结果处理、缓存更新
@AfterThrowing 方法抛出异常后 JoinPoint, 异常对象 异常处理、错误日志
@Around 完全控制方法执行 ProceedingJoinPoint 性能监控、事务管理

最佳实践

💡 AOP最佳实践

🎯 设计原则单一职责- 每个切面只处理一种横切关注点最小侵入- 尽量减少对业务代码的影响性能考虑- 避免在高频方法上使用复杂切面异常处理- 切面中的异常要妥善处理测试友好- 确保切面逻辑可以被单独测试

// AOP配置类
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
    
    /**
     * 配置切面执行顺序
     */
    @Bean
    @Order(1)
    public SecurityAspect securityAspect() {
        return new SecurityAspect();
    }
    
    @Bean
    @Order(2)
    public LoggingAspect loggingAspect() {
        return new LoggingAspect();
    }
    
    @Bean
    @Order(3)
    public PerformanceAspect performanceAspect() {
        return new PerformanceAspect();
    }
}

// 切面测试
@SpringBootTest
class AopTest {
    
    @Autowired
    private UserService userService;
    
    @Test
    void testAopLogging() {
        // 测试AOP日志功能
        User user = userService.getUserById(1L);
        assertNotNull(user);
        assertEquals("user1", user.getUsername());
    }
    
    @Test
    void testAopException() {
        // 测试AOP异常处理
        assertThrows(IllegalArgumentException.class, () -> {
            userService.getUserById(-1L);
        });
    }
}

下一章节

🎉 恭喜完成第14章学习!

你已经掌握了SpringBoot AOP的核心技能,接下来学习缓存机制! 🚀 下一章:SpringBoot Cache 🏠 返回课程首页

🎯 SpringBoot AOP
http://www.clxhxhhr.top/posts/2882/
作者
clxstart
发布于
2026-09-19
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。