首页 > 编程语言 >AOP(面向切面编程)

AOP(面向切面编程)

时间:2024-10-08 21:19:04浏览次数:10  
标签:切入点 编程 切面 AOP 执行 方法 public 表达式

1. AOP 概述

AOP (Aspect-Oriented Programming) 是 Spring 框架的核心功能之一,旨在通过切面来增强程序的功能,特别是在不修改原始代码的情况下为方法添加额外的逻辑,比如日志记录、权限校验、事务管理等。

AOP的作用:在程序运行期间在不修改源代码的基础上对已有方法进行增强(无侵入性: 解耦)

AOP面向切面编程和OOP面向对象编程一样,它们都仅仅是一种编程思想,而动态代理技术是这种思想最主流的实现方式。而Spring的AOP是Spring框架的高级技术,旨在管理bean对象的过程中底层使用动态代理机制,对特定的方法进行编程(功能增强)

举例:

我们要想完成统计各个业务方法执行耗时的需求,我们只需要定义一个模板方法,将记录方法执行耗时这一部分公共的逻辑代码,定义在模板方法当中,在这个方法开始运行之前,来记录这个方法运行的开始时间,在方法结束运行的时候,再来记录方法运行的结束时间,中间就来运行原始的业务方法。

2. AOP 核心概念

Spring的AOP底层是基于动态代理技术来实现的,也就是说在程序运行的时候,会自动的基于动态代理技术为目标对象生成一个对应的代理对象。在代理对象当中就会对目标对象当中的原始方法进行功能的增强。

2.1 连接点 (JoinPoint)

可以被AOP控制的方法(暗含方法执行时的相关信息)

2.2 通知 (Advice)

在连接点执行的代码,可以在方法执行前、后或环绕执行。

在AOP面向切面编程当中,我们只需要将这部分重复的代码逻辑抽取出来单独定义。抽取出来的这一部分重复的逻辑,也就是共性的功能。

2.3 切入点 (PointCut)

匹配连接点的条件,通知仅会在切入点方法执行时被应用。

2.4 切面 (Aspect)

通知和切入点的结合,描述了在哪里、何时执行增强逻辑。

2.5 目标对象(Target)

目标对象指的就是通知所应用的对象,我们就称之为目标对象。

3. AOP使用细节

3.1 切入点表达式

3.1.1 execution

根据方法的签名来匹配。

通过 execution 表达式,可以精确控制在哪些方法上应用 AOP:

execution主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配,语法为:
execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)
其中带 ? 的表示可以省略的部分

@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}

(1)切入点表达式的语法规则:

1. 方法的访问修饰符可以省略

2. 返回值可以使用 * 号代替(任意返回值类型)

3. 包名可以使用 * 号代替,代表任意包(一层包使用一个 * )

4. 使用 .. 配置包名,标识此包以及此包下的所有子包

5. 类名可以使用 * 号代替,标识任意类

6. 方法名可以使用 * 号代替,表示任意方法

7. 可以使用 * 配置参数,一个任意类型的参数

8. 可以使用 .. 配置参数,任意个任意类型的参数

(2)注意事项

根据业务需要,可以使用 且(&&)、或(||)、非(!) 来组合比较复杂的切入点表达式。

execution(* com.itheima.service.DeptService.list(..)) ||
execution(* com.itheima.service.DeptService.delete(..))

(3)切入点表达式的书写建议

1. 所有业务方法名在命名时尽量规范,方便切入点表达式快速匹配。如:查询类方法都是 find 开 头,更新类方法都是update开头

2.描述切入点方法通常基于接口描述,而不是直接描述实现类,增强拓展性

3.在满足业务需要的前提下,尽量缩小切入点的匹配范围。如:包名匹配尽量不使用 ..,使用 * 匹配单个包。

3.1.2 @annotation

根据注解匹配。

如果我们要匹配多个无规则的方法,比如:list() 和 delete()这两个方法。这个时候我们基于execution这种切入点表达式来描述就不是很方便了。 而在之前我们是将两个切入点表达式组合在了一起完成的需求,这个是比较繁琐的。 我们可以借助于另一种切入点表达式annotation来描述这一类的切入点,从而来简化切入点表达式的书写。

还可以通过 @annotation 切入点来根据注解匹配方法:

@Before("@annotation(com.example.annotation.MyLog)")
public void logBefore(JoinPoint joinPoint) {
    log.info("Log before method");
}

实现步骤:

(1)编写自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
}

(2)在业务类要做为连接点的方法上添加自定义注解

3.1.3 抽取切点表达式

Spring提供了@PointCut注解,该注解的作用是将公共的切入点表达式抽取出来,需要用到时引用该切入点表达式即可。

@Slf4j
@Component
@Aspect
public class MyAspect1 {
    //切入点方法(公共的切入点表达式)
    @Pointcut("execution(* com.cyt.service.*.*(..))")
    private void pt(){
     }

    //前置通知(引用切入点)
    @Before("pt()")
    public void before(JoinPoint joinPoint){
    log.info("before ...");
    }
}

注意事项:

当切入点方法使用private修饰时,仅能在当前切面类中引用该表达式, 当外部其他切面类中也要引用当前类中的切入点表达式,就需要把private改为public,而在引用的时候,具体的语法为:

全类名.方法名()

3.2 通知类型

  • @Before:在目标方法执行前执行。
  • @After:在目标方法执行后执行,无论是否发生异常。
  • @AfterReturning:在目标方法成功返回后执行。
  • @AfterThrowing:在目标方法抛出异常后执行。
  • @Around:环绕目标方法执行,既可以在方法前后执行,也可以控制方法是否执行。

3.2.1 注意事项

(1)@Around环绕通知需要自己调用 ProceedingJoinPoint.proceed() 来让原始方法执行,其他通知不需要考虑目标方法执行

(2)@Around环绕通知方法的返回值,必须指定为Object,来接收原始方法的返回值,否则原始方法 执行完毕,是获取不到返回值的。

3.2.2 获取方法信息

在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法 名、方法参数等。

(1)对于@Around通知,获取连接点信息只能使用ProceedingJoinPoint类型

(2)对于其他四种通知,获取连接点信息只能使用JoinPoint,它是ProceedingJoinPoint的父类

3.3 通知顺序

当一个方法匹配多个切面时,通知的执行顺序可以通过 @Order 注解控制:

@Aspect
@Order(1)
public class FirstAspect {
    // 通知代码
}
@Aspect
@Order(2)
public class SecondAspect {
    // 通知代码
}

4. AOP 案例

4.1 需求

统计各个业务层方法执行耗时。

4.2 实现步骤

4.2.1 导入依赖

在pom.xml中导入AOP的依赖。

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

4.2.2 自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {}

4.2.3 添加自定义注解

在业务类要做为连接点的方法上添加自定义注解,即所有的需要统计业务耗时的方法上。

4.2.4 切面类

@Aspect
@Component
public class LogAspect {
    @Around("@annotation(com.example.annotation.Log)")
    public Object logExecutionDetails(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long duration = System.currentTimeMillis() - start;
        // 记录日志
        log.info("Method executed in " + duration + "ms");
        return result;
    }
}

标签:切入点,编程,切面,AOP,执行,方法,public,表达式
From: https://blog.csdn.net/qq_46637011/article/details/142733106

相关文章

  • Java多线程编程基础与高级特性
    在现代软件开发中,多线程编程是一个重要的概念,它能够充分利用多核处理器的能力,提高程序的执行效率。Java语言内置了对多线程的支持,使得开发者可以方便地创建和管理线程。创建线程1.继承Thread类这是最直接的方式,通过创建一个继承自Thread类的子类,并重写run()方法来定义线程......
  • 如何入门STM32单片机编程
    入门STM32单片机编程需要掌握以下内容:STM32的基本概念和特点:了解STM32系列单片机的特点、硬件结构、功能模块以及常用的开发工具和资源。KeilMDK开发环境的安装和使用:KeilMDK是一款非常常用的STM32开发工具,通过安装和配置KeilMDK,可以进行STM32的编译、下载和调试操作。......
  • C#联合Visionpro编程学习记录,视觉中需要考虑旋转中心工况的计算方法探讨
    一、考虑旋转中心的工况解法,1,视觉中引导定位或者对位贴合时,机械手或者xyzr轴上手爪中心和末端轴中心不同轴时,就要考虑旋转中心问题;2,如果设备的CT要求没有很苛刻,可以采用2次拍照的方案解决,1次拍照后纠偏角度,然后在纠正角度后的位置2次拍照纠正x、y偏差; 看下图:第一次拍照得到红......
  • 实验2 C语言分支与循环基础应用编程-1
    任务一#include<stdio.h>#include<stdlib.h>#include<time.h>#defineN5#defineN1397#defineN2476#defineN321intmain(){intcnt;intrandom_major,random_no;srand(time(NULL));//以当前系统时间作为随机种子cnt=0;......
  • 了解final关键字在Java并发编程领域的作用吗?
    在Java并发编程领域,final关键字扮演着一个至关重要的角色。虽然很多同学熟悉final用于修饰变量、方法和类的基本用法,但其在并发环境中的应用和原理却常常被忽视。final关键字不仅仅是一个简单的修饰符,它在多线程编程中确保对象状态的可见性和不变性,这对于构建线程安全的应用至关重......
  • 《Python 安装指南:开启编程之旅》
    《Python安装指南:开启编程之旅》在当今数字化的时代,编程已经成为一项越来越重要的技能。而Python作为一种简洁、高效且功能强大的编程语言,受到了众多开发者的青睐。无论是数据科学、人工智能、Web开发还是自动化脚本编写,Python都能发挥巨大的作用。一、Python的背景......
  • C/C++/EasyX——入门图形编程(6)
    【说明】hello,友友们,这一章是我写的入门图形编程的最后一章了,内容也不多,希望你们学的开心(^~^)。顺便说一下,现在的我对一些基础操作的小游戏挺感兴趣的,之后也许会把代码教程写出来,动个小手点赞支持一下吧。敬请期待ヽ(*≧ω≦)ノ,一:内容介绍这一篇讲的是如何在黑窗口上播放音乐......
  • Python 高级编程:深入解析 CSV 文件读取
    在Python中,读取CSV(逗号分隔值)文件是数据处理中的常见任务。以下将介绍一些高级的方法来读取CSV文件:使用pandas库读取CSV文件importpandasaspddf=pd.read_csv('file.csv')print(df)pandas是一个强大的数据处理库,read_csv函数可以方便地读取CSV文件并将其转换......
  • Python 高级编程:深入探索字符串切片
    在Python中,字符串切片是一种强大的操作,它允许我们从字符串中提取特定的部分。以下是关于Python字符串切片的高级教学: 基本的字符串切片string = "Hello, World!"# 提取从索引 7 到索引 11 的子串(不包括索引 11)substring = string[7:11]print(substring)......
  • labview和QT编程
    LabViewLabView所面向的并非传统意义上的程序员。他的所有功能都可以通过组合某些组件来完成。程序的流程控制,比如循环之类的也是通过画图一样的操作来做的。所有的程序功能几乎都可以通过鼠标来构造出来。优点是做一个能运行的程序非常简单。做一个虚拟的仪器很容易。也不需要......