Spring AOP实现

Spring AOP实现

  • AOP概述
    • 什么是AOP
    • 什么是Spring AOP
  • Spring AOP快速入门
    • 引入依赖
    • 实现计时器
  • Spring AOP详解
    • Spring AOP核心概念
      • 切点(Pointcut)
      • 连接点(Join Point)
      • 通知(Advice)
      • 切面(Aspect)
    • 通知类型
      • 注意事项
    • @PointCut
    • 多个切面
    • 切面优先级 @Order
    • 切点表达式
      • execution表达式
      • @annotation

AOP概述

在这里插入图片描述

什么是AOP

Aspect Oriented Programming(面向切面编程)
什么是面向切面编程,切面指的是某一类特定的问题,所以AOP也可以理解为面向特定方法的编程
简单来说:AOP是一种思想,是对某一类问题的集中处理

什么是Spring AOP

AOP是一种思想,它的实现方法有很多,其中包括Spring AOP,也有AspectJ、CGLIB等

Spring AOP快速入门

引入依赖

在pom.xml文件中添加配置

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

实现计时器

这里我通过一个AOP的实现来记录程序中各个函数执行的时间

package com.example.demo.aspect;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;@Slf4j
@Aspect
@Component
public class TimeAspect {@Around("execution(* com.example.demo.controller.*.*(..))")public Object timeCost(ProceedingJoinPoint joinPoint) throws Throwable {long start = System.currentTimeMillis();Object result = joinPoint.proceed();long end = System.currentTimeMillis();log.info(joinPoint+"消耗时间:{}",end-start+"ms");return result;}
}

执行上述代码
在这里插入图片描述
我们来分析一些这段代码
在这里插入图片描述
这个注解,表明当前类是一个切面类
在这里插入图片描述
这个注解,将该段程序交给spring来管理
在这里插入图片描述
环绕通知,在目标方法的前后都会被执行.后面的表达式表示对哪些方法进行增强
在这里插入图片描述
表示当前需要执行的方法
在这里插入图片描述
表示开始执行当前的方法
在这里插入图片描述
上面代码就解析完成了,接下来,我们开始正式学习AOP的知识

Spring AOP详解

Spring AOP核心概念

切点(Pointcut)

Pointcut的作用就是提供⼀组规则(使用AspectJ pointcut expression language 来描述), 告诉程序对哪些方法来进行功能增强.
在这里插入图片描述
@Around注解里面的就是切入点的表达式

连接点(Join Point)

满足切点表达式规则的方法, 就是连接点. 也就是可以被AOP控制的方法,以入门程序举例, 所有 com.example.demo.controller 路径下的方法, 都是连接点.
切点和连接点的关系
连接点是满足切点表达式的元素. 切点可以看做是保存了众多连接点的⼀个集合

通知(Advice)

通知就是具体要做的工作, 指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)比如上述程序中记录业务方法的耗时时间, 就是通知
在这里插入图片描述

切面(Aspect)

切⾯(Aspect) = 切点(Pointcut) + 通知(Advice),既是整个程序

通知类型

上面我们讲了什么是通知, 接下来学习通知的类型. @Around 就是其中⼀种通知类型, 表示环绕通知.
Spring中AOP的通知类型有以下几种
• @Around: 环绕通知, 此注解标注的通知方法在目标方法前, 后都被执行
• @Before: 前置通知, 此注解标注的通知方法在目标方法前被执行
• @After: 后置通知, 此注解标注的通知方法在目标方法后被执行, 无论是否有异常都会执行
• @AfterReturning: 返回后通知, 此注解标注的通知方法在目标方法后被执行, 有异常不会执行
• @AfterThrowing: 异常后通知, 此注解标注的通知方法发生异常后执行
接下来.我们通过程序来进行学习

package com.example.demo.aspect;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class AspectDemo {@Before("execution(* com.example.demo.controller.*.*(..))")public void doBefore(){log.info("执行AspectDemo doBefore");}@After("execution(* com.example.demo.controller.*.*(..))")public void doAfter(){log.info("执行AspectDemo doAfter");}@AfterReturning("execution(* com.example.demo.controller.*.*(..))")public void doAfterReturning(){log.info("doAfterReturning");}@AfterThrowing("execution(* com.example.demo.controller.*.*(..))")public void doAfterThrowing(){log.info("doAfterThrowing");}@Around("execution(* com.example.demo.controller.*.*(..)) ")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {log.info("doAround 前");Object result = joinPoint.proceed();log.info("doAround 后");return result;}
}
package com.example.demo.controller;import com.example.demo.aspect.MyAspect;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@Slf4j
@RestController
public class HelloController {@RequestMapping("/t1")public String helloTest(){return "t1";}@RequestMapping("/t2")public String testError(){Integer i = 10/0;return "t2";}
}

我们运行程序,我们通过访问t1的url来执行t1的程序
在这里插入图片描述
可以看到,各个注解执行的时间顺序是不一样的
而在程序正常的情况下,@AfterThrowing的通知并没有执行
那么接下来,我们来访问t2的url
结果是出现报错了,我们跳过程序打印的错误日志,可以看到
在这里插入图片描述
执行结果变成了四个

注意事项

• @Around 环绕通知需要调用ProceedingJoinPoint.proceed() 来让原始方法执行, 其他通知不需要考虑目标方法执行.
• @Around 环绕通知方法的返回值, 必须指定为Object, 来接收原始方法的返回值, 否则原始方法执行完毕, 是获取不到返回值的,而且@Around必须要有返回值

@PointCut

上面代码存在⼀个问题, 就是存在大量重复的切点表达式 execution(*
com.example.demo.controller..(…)) , Spring提供了 @PointCut 注解, 把公共的切点表达式提取出来, 需要用到时引用该切入点表达式即可.
下面我们来讲讲怎么使用

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

这里的方法名可以随意取
定义完成,怎么使用呢
我们只需要将pointcut()把其他通知里的切入点表达式给替换掉就行了

package com.example.demo.aspect;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class AspectDemo {@Pointcut("execution(* com.example.demo.controller.*.*(..))")public void pointcut(){}@Before("pointcut()")public void doBefore(){log.info("执行AspectDemo doBefore");}@After("pointcut()")public void doAfter(){log.info("执行AspectDemo doAfter");}@AfterReturning("pointcut()")public void doAfterReturning(){log.info("doAfterReturning");}@AfterThrowing("pointcut()")public void doAfterThrowing(){log.info("doAfterThrowing");}@Around("pointcut() ")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {log.info("doAround 前");Object result = joinPoint.proceed();log.info("doAround 后");return result;}
}

这样就好啦,如果需要在其他切面中使用该切入点的定义时,需要将方法的修饰改为public,同时需要在切入点表达式中,在pointcut()前加上全限定类名,引用方式为: 全限定类名.方法名()

多个切面

我们创建多个切面类,观察一些结果

package com.example.demo.aspect;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class AspectDemo1 {@Before("com.example.demo.aspect.AspectDemo.pt()")public void doBefore(){log.info("执行AspectDemo1 doBefore");}@After("com.example.demo.aspect.AspectDemo.pt()")public void doAfter(){log.info("执行AspectDemo1 doAfter");}
}
package com.example.demo.aspect;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class AspectDemo2 {@Before("com.example.demo.aspect.AspectDemo.pt()")public void doBefore(){log.info("执行AspectDemo2 doBefore");}@After("com.example.demo.aspect.AspectDemo.pt()")public void doAfter(){log.info("执行AspectDemo2 doAfter");}
}
package com.example.demo.aspect;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class AspectDemo3 {@Before("com.example.demo.aspect.AspectDemo.pt()")public void doBefore(){log.info("执行AspectDemo3 doBefore");}@After("com.example.demo.aspect.AspectDemo.pt()")public void doAfter(){log.info("执行AspectDemo3 doAfter");}
}

执行程序,访问t1
在这里插入图片描述
从执行结果可以看出,如果程序中有多个切面,执行顺序是依据切面类的名字来执行的
存在多个切面类时,默认按照切面类的类名字母排序:
• @Before 通知:字母排名靠前的先执行
• @After 通知:字母排名靠前的后执行

切面优先级 @Order

对于多个切面的情况,我们可以利用 @Order来控制他们执行的顺序
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
执行程序,观察结果
在这里插入图片描述
很明显,执行结果如愿得到了优化
@Order 注解标识的切面类, 执行顺序如下:
• @Before 通知:数字越小先执行
• @After 通知:数字越大先执行

切点表达式

上面的代码中, 我们⼀直在使用切点表达式来描述切点. 下面我们来介绍⼀下切点表达式的语法.
切点表达式常见有两种表达方式

  1. execution(……):根据方法的签名来匹配
  2. @annotation(……) :根据注解匹配

execution表达式

execution(<访问修饰符> <返回类型> <包名.类名.方法(方法参数)> <异常>)
其中:访问修饰符和异常可以省略
切点表达式⽀持通配符表达:

  1. '* :匹配任意字符,只匹配⼀个元素(返回类型, 包, 类名, 方法或者方法参数)
    a. 包名使用* 表示任意包(⼀层包使用⼀个*)
    b. 类名使用* 表示任意类
    c. 返回值使用* 表示任意返回值类型
    d. ⽅法名使用* 表示任意方法
    e. 参数使用* 表示⼀个任意类型的参数
  2. … :匹配多个连续的任意符号, 可以通配任意层级的包, 或任意类型, 任意个数的参数
    a. 使用… 配置包名,标识此包以及此包下的所有子包
    b. 可以使用… 配置参数,任意个任意类型的参数

@annotation

首先,我们先自定义一个注解(其他已有的注解也可以)

package com.example.demo.aspect;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MyAspect {}

在我们需要实现功能增强的连接点(方法)上添加我们刚刚创建的注解
这里我们在t1上添加
在这里插入图片描述
然后我们可以再创建一个切面类

package com.example.demo.aspect;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Slf4j
@Aspect
@Component
public class MyAspectDemo {@Before("@annotation(com.example.demo.aspect.MyAspect)")public void doBefore(){log.info("MyAspectDemo doBefore");}@After("@annotation(com.example.demo.aspect.MyAspect)")public void doAfter(){log.info("MyAspectDemo doAfter");}
}

可以看到,代码中,我们的切入点表达式格式是
@annotation(注解的全限定类名+注解名)
接下来,我们执行程序,注意,这里需要屏蔽其他切面的影响
访问t1
在这里插入图片描述
程序执行成功了
然后我们执行t2
可以看到,日志并没有t2的相关结果
那么到这里,AOP的使用,我就分享完了,感谢大家的支持

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

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

相关文章

Ubuntu 隐藏Telnet主机SSH服务时显示版本信息问题

一、背景 默认情况下&#xff0c;我们通过telnet服务器的22端口&#xff0c;能够获取OpenSSH服务的banner信息(如下图所示)。而低版本的OpenSSH存在许多高危漏洞。。为了安全我们要隐藏这个信息。 二、隐藏Telnet版本信息 当使用telnet命令&#xff0c;telnet 192.168.31.20…

前缀和、差分

0x03前缀和、差分 文章目录 0x03前缀和、差分一维前缀和二维前缀和差分一维差分二维差分 习题T1T2T3 一维前缀和 数组前n项和 s [ k ] ∑ i 1 k a [ i ] s[k]\sum_{i1}^ka[i] s[k]∑i1k​a[i] s[i]s[i-1]a[i];二维前缀和 设s[i][j]表示以(1&#xff0c;1)为顶点&#xff0…

【Python实战】Python多线程批量采集图片

前言 本文来介绍如何多线程采集图片&#xff0c;多线程效率更快&#xff0c;但是&#xff0c;我们单一IP请求过于频繁&#xff0c;可能会被反爬&#xff0c;被封IP&#xff0c;所以&#xff0c;我们就要用到IP代理池&#xff0c;这里&#xff0c;我给大家推荐一个&#xff0c;可…

5.10 动态优先级模式

动态优先级模式与静态优先级模式类似&#xff0c;但动态优先级模式会随着任务运行时条件的改变自动调整任务优先级。调整任务优先级的策略有很多种&#xff0c;最常见的是“最早截止期优先”(Earliest Deadline First)&#xff0c;即优先级最高的任务是离截止时间最近的任务。动…

11个技巧让你的Python性能起飞

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com Python 是一门非常强大且易于学习的编程语言&#xff0c;但在处理大规模数据或复杂任务时&#xff0c;可能会遇到性能瓶颈。为了让你的 Python 代码运行更快&#xff0c;本文将介绍一些提高 Python 性能的技巧和…

uniapp如何添加多个表单数组?

目录 一、实现思路 二、实现步骤 ①view部分展示 ②JavaScript 内容 ③css中样式展示 三、效果展示 四、小结 注意事项 总结模板&#xff1a; 一、实现思路 1.在 data 中定义一个数组&#xff0c;用于存储表单项的数据 2.在模板中使用 v-for 指令渲染表单项 3.在 methods 中…

如何快速搭建个人博客

说明&#xff1a;本文介绍如何使用Halo框架快速搭建个人博客&#xff0c;我用的是阿里云的云服务器&#xff0c;操作系统是CentOS 7.0。 安装Docker&DockerCompose &#xff08;1&#xff09;Docker安装 Docker的安装参考&#xff0c;Docker安装&卸载&#xff1b; …

AI算力专题:算力系列之四-各省算力规划建设梳理-绿色低碳高质量发展-部署算力建设AI产业研究

今天分享的是AI算力系列深度研究报告&#xff1a;《AI算力专题&#xff1a;算力系列之四-各省算力规划建设梳理-绿色低碳高质量发展-部署算力建设AI产业研究》。 &#xff08;报告出品方&#xff1a;中泰证券&#xff09; 报告共计&#xff1a;40页 数据中心能耗情况 随着越…

【Docker】linux、nginx、容器镜像三者基本概念

欢迎来到《小5讲堂》&#xff0c;大家好&#xff0c;我是全栈小5。 这是《Docker容器》序列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对…

PCB设计10条重要布线原则(学习笔记)

文章目录 一、连线精简二、避免走直角线三、差分走线四、蛇形走线五、圆滑走线六、数字与模拟分开七、3W原则八、20H原则九、铜箔承载电流十、过孔承载电流 一、连线精简 尽量用最短的路径去布线 1、可以省资源 2、信号差损少 3、线能不拐弯就不拐弯 4、能不换层就不换层 二…

SQL中实现行列转换

目录 方法一&#xff1a;sum case when 方法二&#xff1a;sum if 方法三&#xff1a;pivot 现在有一张表class_gender&#xff0c;内容如下&#xff1a; classgender一年级女一年级女一年级男一年级男二年级女二年级女二年级男 现在我们要根据上表&#xff0c;统计得到下…

初识attention

近年来&#xff0c;attention机制在机器视觉和机器翻译领域受到了广泛的关注&#xff0c;有很多文章都是融合attention来提高性能。attention受启发于人类的视觉系统&#xff0c;最先应用于序列化的机器翻译(NLP)后又推广到计算机视觉中&#xff0c;本篇文章就来简单学习一下at…

JS基础 - 遍历对象方法(6种)

初始值&#xff1a; var obj {a: 1,b: 2,c: 3,d: 4,e: 5,}; 第一种&#xff1a;for in for (let key in obj) {console.log(key ":" obj[key]);} 第二种&#xff1a;Object.keys 获取key Object.keys(obj).forEach((key) > {console.log(key ":" …

AI绘画:PhotoMaker Win11本地安装记录!

昨天介绍一个叫PhotoMaker的AI绘画开源项目。挺不错的&#xff01; 通过这个项目可以快速制作特定人脸的AI绘画作品&#xff0c;相比传统的技术效果会好很多&#xff0c;效率也高很多。 今天趁热打铁&#xff0c;本地电脑装装看&#xff0c;并且记录&#xff0c;分享一下&#…

js padEnd方法介绍笔记

padEnd() 是 JavaScript 字符串的方法之一&#xff0c;它用于在当前字符串的末尾添加指定数量的字符&#xff0c;直到字符串达到指定的长度。 padEnd() 方法接受两个参数&#xff1a; 目标长度和要添加的填充字符。 如果当前字符串的长度已经等于或超过目标长度&#xff0c;则不…

程序员怎么写简历_写简历软件

你们在制作简历时&#xff0c;是不是基本只关注两件事&#xff1a;简历模板&#xff0c;还有基本信息的填写。 当你再次坐下来更新你的简历时&#xff0c;可能会发现自己不自觉地选择了那个“看起来最好看的模板”&#xff0c;填写基本信息&#xff0c;却没有深入思考如何使简历…

vue预览pdf文件的几种方法

文章目录 vue预览pdf集中方法方法一&#xff1a;方法二&#xff1a;展示效果&#xff1a;需要包依赖&#xff1a;代码&#xff1a; 方法三&#xff1a;展示效果&#xff1a;需要包依赖&#xff1a;代码&#xff1a;自己调参数&#xff0c;选择符合自己的 vue预览pdf集中方法 我…

第 9 章:理解C++内存模型

本章是继[第8章]讨论的延续&#xff0c;我们在那里讨论了一些多进程和多线程技术&#xff1b;本章将增强它们的使用。我们将引导你了解各种技术&#xff0c;同时聚焦于本章的主要内容——C内存模型。但为了讨论这一点&#xff0c;你首先将简要检视通过智能指针和可选对象实现的…

ZK鉴权设计以及相关探讨

文章目录 1. zk的鉴权设计2. zk鉴权应用范围3. zk鉴权的常用方法4. 推荐配置5. 参考文档 鉴权&#xff0c;分别由鉴和权组成 鉴&#xff1a; 表示身份认证&#xff0c;认证相关用户是否存在以及相关的用户名和密码是否一致权&#xff1a; 完成身份的鉴后&#xff0c;还需要判断…

DolphinScheduler + Amazon EMR Serverless 的集成实践

01 背景 Apache DolphinScheduler 是一个分布式的可视化 DAG 工作流任务调度开源系统&#xff0c;具有简单易用、高可靠、高扩展性、⽀持丰富的使用场景、提供多租户模式等特性。适用于企业级场景&#xff0c;提供了一个可视化操作任务、工作流和全生命周期数据处理过程的解决方…