Springboot AOP开发

news2024/12/28 20:31:20

Springboot AOP开发

一 AOP概述

AOP,即面向切面编程,简言之,面向方法编程。

针对方法,在方法的执行前或执行后使用,用于增强方法,或拓展。

二 AOP开发

1.引入 spring-boot-starter-aop

在SpringBoot项目的pom文件中,引入 spring-boot-starter-aop依赖。

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

2.示例:计算方法执行时间

1.创建实体类,通过注解来申明该类的类型,并将该类交给Spring的IOC容器来管理。

通过注解 @Aspect 申明这是一个AOP类

通过 @Component 将其交给IOC容器管理

@Aspect
@Component
public class TimeAspect {
    //code
}

2.创建方法并且实现

@Aspect
@Component
@Slf4j
public class TimeAspect {
    // 针对 com.shawn.springboot03.service 包下所有的方法进行编程,
    // * com.shawn.springboot03.service.*.*(..)) 为切入点表达式
    @Around("execution(* com.shawn.springboot03.service.*.*(..))")
    public Object recordTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long startTime=System.currentTimeMillis();
        // 切面对象,执行具体的业务方法
        Object object = proceedingJoinPoint.proceed();
        long endTime=System.currentTimeMillis();
        log.info("方法耗时为{}ms",endTime-startTime);
        return object;
    }
}

通过以上方法,当用户调用 service 层接口的任一方法时都会计算方法的运行时间。

3.AOP编程的优点:

  • 代码无侵入:无需修改原始方法
  • 减少代码重复,提高开发效率:只需编写一次
  • 维护方便:根据业务需求,调整切入点表达式即可

三 AOP详解

1.AOP核心概念

1.连接点(JoinPoint):连接点指的是可以被AOP控制的方法,以及方法执行时的相关信息。

2.通知(Advice):Advice指的是被抽取出来的共性功能,即重复的那部分逻辑。

3.切入点(PointCut):匹配连接点的条件,通知仅会在切入点方法执行时被应用

4.切面(Aspcet):描述通知与切入点的关系

5.目标对象(Target):通知所应用的对象

2.AOP通知类型

  • @Around:环绕通知,此注解标注的方法在目标方法前后都会执行
  • @Before:前置通知,此注解标注的方法仅在方法执行前被执行
  • @After:后置通知,此注解在方法执行完成后执行,不论是否抛出异常
  • @AfterReturning:返回后通知,此注解标注的方法在目标方法后被执行,有异常不通知
  • @AfterThrowing:异常后通知,此注解的通知方法发生异常后执行

3.各通知类型演示

创建一个 TestAspect 类,用于演示各种通知类型

  1. @Around 通知类型

    介绍:在方法前后均执行

    切入点表达式格式: 返回值 包名.方法名(形参)

    其中 * 代表全部,.. 代表任意多的参数

    切入点表达式示例说明:

    * com.shawn.test.server.*(..) 表示 任意返回值的 com.shawn.test.server包下任意返回值,任意多参数的全部方法。

    @Aspect
    @Component
    @Slf4j
    public class TestAspect {
        @Around("execution(* com.shawn.springboot03.service.impl.DeptServiceImpl.*(..))")
        public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            
            //方法执行前的业务逻辑
            //code..
            
            //执行原始方法(即连接点),并且接收原始方法的返回值
            Object proceed = proceedingJoinPoint.proceed();
            
            //方法执行后的业务逻辑
            //code..
            
            //返回原始方法的返回值
            return proceed;
        }
    }
    

    ProceedingJoinPoint是一个接口类,指代程序执行过程中的一个特定点,其中包含了原始方法的全部内容,包括方法名,参数值等。

    在使用 @Around通知类型时,需要将该对象作为参数传递进来,用于后续执行原始方法或获取原始方法的其他信息。

    ProceedingJoinPoint仅能作用于 @Around 类型的通知上。

2.@Before 通知类型

介绍:此注解标注的方法仅在原始方法执行前执行

@Aspect
@Component
@Slf4j
public class TestAspect {
    @Before("execution(* com.shawn.springboot03.service.*.*(..))")
    public void before(){
        // 方法执行前的业务逻辑
        //code..
    }
}

3.@After 通知类型

介绍:此注解标注的方法,仅在原始方法执行后执行,且不论原始方法是否执行,该通知都会执行

@Aspect
@Component
@Slf4j
public class TestAspect {
    @After("execution(* com.shawn.springboot03.service.*.*(..))")
    public void after(){
        // 方法执行后的业务逻辑
        //code..
    }
}

4.@AfterReturning 通知类型

介绍:此注解标注的方法,仅在原始方法执行完成后通知,即当原始方法执行发生异常时,@AfterReturning 通知不会被执行。

@Aspect
@Component
@Slf4j
public class TestAspect {
    @AfterReturning("execution(* com.shawn.springboot03.service.*.*(..))")
    public void afterReturning(){
        //方法执行完成的业务逻辑
        //code..
    }
}

5.@AfterThrowing通知类型

介绍:次注解标注的方法,仅在原始方法执行过程中发生了异常,才会执行。

@Aspect
@Component
@Slf4j
public class TestAspect {
    @AfterThrowing("execution(* com.shawn.springboot03.service.*.*(..))")
    public void afterThrowing(){
        //方法执行时抛出异常的业务逻辑
        //code..
    }
}

4.定义切入点

以上示例中的切入点表达式均相似,可以利用封装的思想,将切入点表达式全部抽取出来,在需要的时候直接使用即可。

想要实现以上需求,则需要自定义切入点表达式

通过 @Pointcut 注解,来定义切入点表达式,然后在需要编写切入点表达式的地方调用即可。

@Aspect
@Component
@Slf4j
public class TestAspect {
    /**
     * 声明一个空的方法体来定义切入点表达式
     * 使用 @Pointcut 注解来定义切入点表达式
     */
    @Pointcut("execution(* com.shawn.springboot03.service.impl.DeptServiceImpl.*(..))")
    private void point(){};
    
    @After("point()")
    public void after(){
        // 方法执行后的业务逻辑
        //code..
    }
}

注意:如果自定义的切入点访问修饰符为 public ,则该表达式还可以在其他切面类中被引用。具体使用可根据实际业务情况决定。

定义切入点总共有两种办法,一种是通过方法名来定义切入点,还可以通过注解来定义切入点。

示例:以下示例使用自定义注解作为切入点,使用了以下自定义注解的方法会执行通知。

@Aspect
@Component
public class TestAspect {
    @Pointcut("@annotation(com.shawn.springboot03.annotation.OperationLog)")
    private void point(){};

    @AfterThrowing("point()")
    public void afterThrowing(){
        //方法执行时抛出异常的业务逻辑
        //code..
    }
}

5.通知顺序

当有多个切入点都匹配到了目标方法,目标方法运行时,多个通知都会被执行。

某些情况下则需要控制通知的顺序,可以在切面类上使用 @Order 注解来控制顺序

@Aspect
@Component
@Slf4j
@Order(3)
public class TestAspect {
    @Pointcut("execution(* com.shawn.springboot03.service.impl.DeptServiceImpl.*(..))")
    private void point(){};

    @After("point()")
    public void after(){
        // 方法执行后的业务逻辑
        //code..
    }
}

注意:@Order(number) 注解中,数字越小的越先运行,越大的越后运行。

默认情况下,按照切面类的类名排序顺序执行。

四 切入点表达式

1.execution

execution 主要根据方法的返回值,包名类名,方法名,方法参数等信息来匹配,语法为:

execution( 访问修饰符? 返回值 包名.类名.?方法名(方法参数) throw 异常? )

其中带 ? 的表示可以省略的部分

  • 访问修饰符:可省略(比如:public,protected)
  • 包名.类名:可省略
  • throws 异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)

以下是一个完整示例:

@Before ("execution(public void com.itheima.service.impl.DeptserviceImpl.delete (java.lang.Integer)) throws Exception")
private void point(){};

省略后的示例:

@Before ("execution(void delete (java.lang.Integer))")
private void point(){};

此时,所有 void delete (java.lang.Integer) 方法都将被匹配到。

注意: * 用于描述匹配单个, … 可以用来匹配多个连续,?表示可省略
在这里插入图片描述

2.@annotation

@annotation 切入点表达式,用于匹配标识有特定注解的方法。

示例:

@Pointcut("@annotation(com.shawn.annotation.OperationLog)")
private void point(){};

五 连接点

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

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

@Around("point()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    //获取目标类名
    String name = proceedingJoinPoint.getTarget().getClass().getName();
    log.info("获取目标类名:"+name);
    //获取方法执行参数
    Object[] args = proceedingJoinPoint.getArgs();
    log.info("获取方法执行参数:"+JSON.toJSONString(args));
    //获取目标方法名
    String methodName = proceedingJoinPoint.getSignature().getName();
    log.info("获取目标方法名:"+methodName);

    //执行原始方法(即连接点),并且接收原始方法的返回值
    Object proceed = proceedingJoinPoint.proceed();
    
    //获取方法的返回值
    log.info("获取方法的返回值:"+JSON.toJSONString(proceed));

    //返回原始方法的返回值
    return proceed;
}

proceedingJoinPoint.proceed()得到目标方法的结果再返回,此处可以对目标方法执行的结果进行篡改。

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

@Before("point()")
public void before(JoinPoint joinPoint){
    // 获取目标方法的类名
    String name = joinPoint.getTarget().getClass().getName();
    log.info("获取目标方法的类名:"+name);
    String methodName = joinPoint.getSignature().getName();
    log.info("获取目标方法的方法名:"+methodName);
    //获取目标方法的运行参数
    Object[] args = joinPoint.getArgs();
    log.info("获取目标方法的参数:"+JSON.toJSONString(args));
}

3.除@Around 通知外,还可以获取到方法执行结果的通知类型为 @AfterReturning,@AfterReturning 在方法执行完成并且无异常时通知。

在定义@AfterReturning通知类型时,使用 pointcut 属性定义切入点,returning属性定义返回值对象,然后在方法参数中传入即可。

@AfterReturning(pointcut = "point()",returning = "object")
public void afterReturning(JoinPoint joinPoint, Object object){
    // 获取目标方法的类名
    String name = joinPoint.getTarget().getClass().getName();
    log.info("获取目标方法的类名:"+name);
    String methodName = joinPoint.getSignature().getName();
    log.info("获取目标方法的方法名:"+methodName);
    //获取目标方法的运行参数
    Object[] args = joinPoint.getArgs();
    log.info("获取目标方法的参数:"+JSON.toJSONString(args));
    //获取方法返回值
    log.info("获取方法返回值:"+JSON.toJSONString(object));
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1460366.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机视觉基础【OpenCV轻松入门】:获取图像的ROI

OpenCV的基础是处理图像&#xff0c;而图像的基础是矩阵。 因此&#xff0c;如何使用好矩阵是非常关键的。 下面我们通过一个具体的实例来展示如何通过Python和OpenCV对矩阵进行操作&#xff0c;从而更好地实现对图像的处理。 ROI&#xff08;Region of Interest&#xff09;是…

十三、集合进阶——单列集合 及 数据结构

单列集合 及 数据结构 13.1 集合体系结构13.1.2 单列集合1. Collection2.Collection 的遍历方式迭代器遍历增强for遍历Lambda表达式遍历 3.List集合List集合的特有方法List集合的遍历方式五种遍历方式对比 4.数据结构1).栈2).队列3&#xff09;数组4&#xff09;链表小结5&…

【程序员英语】【美语从头学】初级篇(入门)(笔记)Lesson 15 At the Department Store 在百货商店

《美语从头学初级入门篇》 注意&#xff1a;被 删除线 划掉的不一定不正确&#xff0c;只是不是标准答案。 文章目录 Lesson 15 At the Department Store 在百货商店会话A会话B笔记 Lesson 15 At the Department Store 在百货商店 会话A A: Can you help me, please? B: Sur…

世强硬创与SMT激光模板制造商光盛激光达成平台合作

近日&#xff0c;世强先进&#xff08;深圳&#xff09;科技股份有限公司&#xff08;下称“世强先进”&#xff09;与一家专注生产研发电子工业用工模具的企业——东莞光盛激光科技有限公司&#xff08;下称“光盛激光”&#xff09;达成平台合作。 据了解&#xff0c;光盛激光…

Facebook与数字创新:引领社交媒体的数字化革命

在当今数字化时代&#xff0c;社交媒体已经成为了人们日常生活中不可或缺的一部分。而在众多社交媒体平台中&#xff0c;Facebook作为领头羊&#xff0c;一直致力于推动数字创新&#xff0c;引领着社交媒体的数字化革命。本文将探讨Facebook在数字创新方面的表现&#xff0c;以…

绝地求生:图纸的加量不加价是否预示着蓝洞经营模式的转变

成长型武器目前作为PUBG中除了究极异色皮肤外的最高等级武器&#xff08;传说级&#xff09;&#xff0c;也是PUBG核心利润来源&#xff0c;十分的珍贵。 一把成长型武器的保底价格为3000碎片&#xff0c;而每次通过G-coin抽取会赠送10个碎片&#xff0c;也就是需要抽取三百次&…

外汇交易解决方案丨实时选取外汇行情多价源最优价

在外汇交易中&#xff0c;存在多个价源。多个价源之间&#xff0c;同一时刻的报价可能存在差异。在多个价源之间&#xff0c;实时选取最优价格具有重要意义&#xff0c;它有助于投资者获得更有利的交易执行价&#xff0c;降低交易成本&#xff0c;优化流动性&#xff0c;分散交…

中科大计网学习记录笔记(十四):多路复用与解复用 | 无连接传输:UDP

前言&#xff1a; 学习视频&#xff1a;中科大郑烇、杨坚全套《计算机网络&#xff08;自顶向下方法 第7版&#xff0c;James F.Kurose&#xff0c;Keith W.Ross&#xff09;》课程 该视频是B站非常著名的计网学习视频&#xff0c;但相信很多朋友和我一样在听完前面的部分发现信…

YOLO-NAS浅析

YOLO-NAS&#xff08;You Only Look Once - Neural Architecture Search&#xff09;是一种基于YOLO&#xff08;You Only Look Once&#xff09;的目标检测算法&#xff0c;结合神经架构搜索&#xff08;NAS&#xff09;技术来优化模型性能。 YOLO是一种实时目标检测算法&…

攻防世界-web-Training-WWW-Robots

题目信息 In this little training challenge, you are going to learn about the Robots_exclusion_standard. The robots.txt file is used by web crawlers to check if they are allowed to crawl and index your website or only parts of it. Sometimes these files rev…

pytest框架学习总结:失败用例如何处理?

当我们跑用例的时候&#xff0c;有些用例可能会失败&#xff0c;可以对失败的用例设置做如下管理&#xff1a; 1、失败重跑: --reruns 2 --reruns-delay 5 2、失败了停止执行后续的用例&#xff1a;pytest -x 3、设置最多失败多少用例会停止执行:pytest --maxfail2 4、跳过用例…

《Solidity 简易速速上手小册》第7章:智能合约的部署与交互(2024 最新版)

文章目录 7.1 合约的编译和部署7.1.1 基础知识解析更全面的理解部署准备 7.1.2 重点案例&#xff1a;部署一个投票合约案例 Demo&#xff1a;创建并部署投票合约案例代码VotingContract.sol部署脚本&#xff08;Truffle&#xff09; 测试和验证拓展功能 7.1.3 拓展案例 1&#…

基于vue框架的环保知识普及平台设计与实现

项目&#xff1a;基于vue框架的环保知识普及平台设计与实现 目 录 摘要 I Abstract II 第1章 引言 1 1.1 研究的背景 1 1.2 目的和意义 1 1.3 设计思路 1 1.4 研究的主要内容 2 第2章 相关原理和技术 3 2.1 B/S 模式体系结构 3 2.2 Springboot技术 4 2.3 访问数据库…

GitHub 2FA认证(双重身份验证)

GitHub 2FA认证&#xff08;双重身份验证&#xff09; GitHub 向部分用户发出警告&#xff1a;如果在北京时间 2024 年 1 月 19 日 08:00 前仍未启用双重验证&#xff08;2FA)&#xff0c;他们将被禁用部分功能。当然&#xff0c;这对于 GitHub 用户来说并不算意外&#xff0c…

Leetcode 1089.复写零

目录 题目 思路 代码 题目 给你一个长度固定的整数数组 arr &#xff0c;请你将该数组中出现的每个零都复写一遍&#xff0c;并将其余的元素向右平移。 注意&#xff1a;请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改&#xff0c;不要从函数返回…

录屏后没有声音怎么补救?试试这几种录屏工具

录屏后没有声音怎么补救&#xff1f;在数字化时代&#xff0c;无论是为了制作教学视频、分享游戏过程&#xff0c;还是为了记录会议内容&#xff0c;录屏都扮演着重要的角色。然而&#xff0c;在进行录屏时&#xff0c;有时可能会遇到录屏后没有声音的问题&#xff0c;这无疑会…

MIT 6.S081---Lab: Copy-on-Write Fork for xv6

Implement copy-on write(hard) 从下图可见&#xff0c;xv6的pte中RSW都是可以被软件使用的未定义位&#xff0c;此处将pte中的第9位标记为PTE_COW位。 修改kernel/riscv.h&#xff1a; 修改kernel/vm.c&#xff0c;新增refCount代表物理页面的引用次数&#xff0c;根据&…

2024年2月的TIOBE指数,go语言排名第8,JAVA趋势下降

二月头条&#xff1a;go语言进入前十 本月&#xff0c;go在TIOBE指数前10名中排名第8。这是go有史以来的最高位置。当谷歌于2009年11月推出Go时&#xff0c;它一炮而红。在那些日子里&#xff0c;谷歌所做的一切都是神奇的。在Go出现的几年前&#xff0c;谷歌发布了GMail、谷歌…

探索水下低光照图像检测性能,基于DETR(DEtection TRansformer)模型开发构建海底生物检测识别分析系统

海底这类特殊数据场景下的检测模型开发相对来说比较少&#xff0c;在前面的博文中也有一些涉及&#xff0c;感兴趣的话可以自行移步阅读即可&#xff1a; 《尝试探索水下目标检测&#xff0c;基于yolov5轻量级系列模型n/s/m开发构建海底生物检测系统》 《基于YOLOv5C3CBAMCBA…

list链表

1. list基本概念 功能&#xff1a;将数据进行链式存储 链表&#xff08;list&#xff09;是一种物理存储单元上非连续的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接实现的 链表的组成&#xff1a;链表由一系列结点组成 结点的组成&#xff1a;一个是存储数据…