Spring AOP讲解及实例

Aop面向切面编程

文章目录

  • Aop面向切面编程
    • 什么是AOP
    • AOP术语
    • Spring AOP 的使用
      • 导入依赖
      • 编写切面类
      • 切面定义语法
      • 小细节
      • 输出日志成功

什么是AOP

AOP:(Aspect Oriented Programming)面向切面编程,和OOP(Object Oriented Programming)面向对象编程一样,也是计算机开发的一种程序设计思想,与OOP将程序中的每个环节对象化,如实体类等相比,面向切面就是在不改变程序现有代码的前提下,可以设置某方法运行前或运行后新增额外代码的操作,减少对代码的入侵。包括过滤器、拦截器都是一种AOP的思想,只不过Spring AOP是Spring给的,(注:AOP并不是Spring框架独有的,而是从AspectJ框架中借鉴而来)过滤器是Java给的,拦截器是SpringMVC给的。

对AOP官方文档翻译:

AOP目标是将横切关注点与业务主体进行进一步分离,以提高程序代码的模块化程度。通过在现有代码基础上增加额外的通知Advice)机制,能够对被声明为“切点“(Pointcut)的代码块进行统一管理与扩展

AOP术语

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EqfMpLBJ-1688283328056)(../AppData/Roaming/Typora/typora-user-images/image-20230702134627970.png)]

  • 切面(Aspect):是一个可以加入额外代码运行的特定位置,一般指方法之间的调用,可以在不修改原代码的情况下,添加新的代码,对现有代码进行升级维护和管理

  • 织入(Weaving):选定一个切面,利用动态代理技术,为原有的方法的持有者生成动态对象,然后将它和切面关联,在运行原有方法时,就会按织入之后的流程运行了

    • 动态代理:

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wOw6mwgq-1688283328057)(../AppData/Roaming/Typora/typora-user-images/image-20230702135722664.png)]

  • 目标对象(Target):需要被加强的业务对象

  • 代理类(Proxy) 一个类被AOP织入通知后,就产生了一个代理类。

  • 切点(PointCut):每个程序的连接点有多个,如何定位到某个感兴趣的连接点,就需要通过切点来定位。

  • 连接点(Joinpoint):程序执行的某个特定位置,如某个方法调用前,调用后,方法抛出异常后,这些代码中的特定点称为连接点。简单来说,就是在哪加入你的通知

    • 连接点表示具体要拦截的方法,切点是定义一个范围,而连接点是具体到某个方法
  • 通知(Advice):原意为增强,是织入到目标类连接点上的一段程序代码。

    分以下几种:

    • 前置通知(before):执行业务代码前做些操作,比如获取连接对象
    • 后置通知(after):在执行业务代码后做些操作,无论是否发生异常,它都会执行,比如关闭连接对象
    • 异常通知(afterThrowing):在执行业务代码后出现异常,需要做的操作,比如回滚事务
    • 返回通知(afterReturning):在执行业务代码后无异常,会执行的操作
    • 环绕通知(around):

Spring AOP 的使用

导入依赖

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

编写切面类

@Slf4j
@Aspect    //切面
@Component
public class LogAspect {private HashMap<String, Object> map = new HashMap<>();private final String filePath = "logFile.txt";//1.定义切面/*** 此步骤单纯定义切面,就是指定一个方法* 可以在这个方法运行前或运行后等位置织入额外代码** 匹配com.liner.controller包及其子包下的所有类的所有方法*/@Pointcut("execution(public * com.liner.controller.*.*(..))")// 切入点    执行    公有方法 任意返回值    包路径   任何类 任何方法 任何参数public void pointCut() {}//2.织入内容/*** 前置通知,目标方法调用前被调用* 向切面方法前添加代码,在方法运行前输出指定内容*/@Before("pointCut()")public void beforeAdvice(JoinPoint joinPoint) { //JoinPoint 会包含当前切面的各种信息,连接点log.info("----------- 前置通知 -----------");Signature signature = joinPoint.getSignature(); //获得当前切面对应方法的信息log.info("返回目标方法的签名:{}", signature);log.info("代理的是哪一个方法:{}", signature.getName());Object[] args = joinPoint.getArgs();log.info("获取目标方法的参数信息:{}", Arrays.asList(args));map.put("方法签名", signature);map.put("代理方法", signature.getName());map.put("参数信息", Arrays.asList(args));}/*** 后置通知,也叫最终通知,目标方法执行完之后执行*/@After("pointCut()")public void afterAdvice() {log.info("----------- 后置通知 -----------");}/*** 后置返回通知* 如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息* 如果参数中的第一个参数不为JoinPoint,则第一个参数为returning中对应的参数* returning 只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知,否则不执行** @param joinPoint* @param keys*/@AfterReturning(pointcut = "pointCut()", returning = "keys")public void afterReturningAdvice(JoinPoint joinPoint, String keys) {log.info("~~~~~~~~~~ 后置返回通知 ~~~~~~~~~~");log.info("后置返回通知的返回值:{}", keys);}/*** 后置异常通知* 定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法;* throwing 只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行,** @param e*/@AfterThrowing(value = "pointCut()", throwing = "e")public void afterThrowingAdvice(Exception e) {log.info("~~~~~~~~~~ 后置异常通知 ~~~~~~~~~~");log.info(e.toString());}/*** 环绕通知* 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。* 环绕通知第一个参数必须写,且是org.aspectj.lang.ProceedingJoinPoint类型  JoinPoint的子接口,拥有更多方法* 这个方法还要有返回值,因为调用的切面的方法可能有返回值,环绕advice如果不返回这个值,调用者就接收不到这个值了* @param proceedingJoinPoint*/@Around(value = "pointCut()")public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {log.info("----------- 环绕通知 -----------");log.info("环绕通知的目标方法名:{}", proceedingJoinPoint.getSignature().getName());map.put("目标方法", proceedingJoinPoint.getSignature().getName());try {return proceedingJoinPoint.proceed();   //ProceedingJoinPoint类型的参数具有调用切面方法的功能} catch (Throwable throwable) {throwable.printStackTrace();} finally {log.info("---------- 环绕通知结束 -------------");writeLog(map);}return null;}/*** 打印日志* @param map*/void writeLog(HashMap<String, Object> map) {try {long currentTimeMillis = System.currentTimeMillis();SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日-hh时mm分ss秒");Date date = new Date(currentTimeMillis);FileWriter fw = new FileWriter(filePath,true);fw.write("----------- 环绕通知 -----------"+    format.format(date)+  "\r\n");fw.write("环绕通知的目标方法名:" + (String) map.get("目标方法") +      "\r\n");fw.write("----------- 前置通知 -----------"+                          "\r\n");fw.write("返回目标方法的签名:" + (String) map.get("方法签名").toString() +        "\r\n");fw.write("代理的是哪一个方法:" + (String) map.get("代理方法") +        "\r\n");fw.write("获取目标方法的参数信息:" + (String) map.get("参数信息").toString() +    "\r\n");fw.write("----------- 最终通知 -----------"+                          "\r\n");fw.write("---------- 环绕通知结束 -------------"+                     "\r\n");fw.flush();fw.close();} catch (IOException e) {e.printStackTrace();}}}

切面定义语法

 @Pointcut("execution(public * com.liner.controller.*.*(..))")

下面介绍切面定义表达式的详细语法规则

语法模板:

execution(
modifier-pattern?
ret-type-pattern
declaring-type-pattern?
name-pattern(param-pattern)
throws-pattern?
)

?的是可选属性,不带?是必须写的

  • modifier-pattern:访问修饰符(可选)
  • ret-type-pattern:返回值类型(必写),一般为*
  • declaring-type-pattern:全路径类名(可选)
  • name-pattern(param-pattern):方法名(必写)
  • param-pattern:参数列表(必写),一般为..
  • throws-pattern:抛出的异常类型(可选)

小细节

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DVE36Unh-1688283328058)(../AppData/Roaming/Typora/typora-user-images/image-20230702143314638.png)]

几种通知图标不同:

  • 前置通知(before):

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gs7nXB4n-1688283328059)(../AppData/Roaming/Typora/typora-user-images/image-20230702143521568.png)]

  • 后置通知(after):

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bvYpIIpl-1688283328060)(../AppData/Roaming/Typora/typora-user-images/image-20230702143808539.png)]

  • 异常通知(afterThrowing):

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NGPiH1E2-1688283328061)(../AppData/Roaming/Typora/typora-user-images/image-20230702144114022.png)]

  • 返回通知(afterReturning):

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BKLGGMgj-1688283328061)(../AppData/Roaming/Typora/typora-user-images/image-20230702144010730.png)]

  • 环绕通知(around):

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3NAaxAvc-1688283328062)(../AppData/Roaming/Typora/typora-user-images/image-20230702144223571.png)]

输出日志成功

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YfcEvstE-1688283328062)(../AppData/Roaming/Typora/typora-user-images/image-20230702142807461.png)]

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

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

相关文章

Vue3 网络请求——axios 高级用法之 axios 拦截器实战与并发请求

文章目录 &#x1f4cb;前言&#x1f3af;关于拦截器&#x1f3af;项目创建&#x1f3af;代码分析&#x1f3af;补充&#xff1a;并发请求&#x1f9e9;axios.all() 和 Promise.all() 的区别 &#x1f4dd;最后 &#x1f4cb;前言 Axios 是一个流行的基于 Promise 的 HTTP 客户…

设计模式3:单例模式:静态内部类模式是怎么保证单例且线程安全的?

上篇文章&#xff1a;设计模式3&#xff1a;单例模式&#xff1a;静态内部类单例模式简单测试了静态内部类单例模式&#xff0c;确实只生成了一个实例。我们继续深入理解。 静态变量什么时候被初始化&#xff1f; public class Manager {private static class ManagerHolder …

【SQL应知应会】行列转换(三)• Oracle版

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 本文收录于SQL应知应会专栏,本专栏主要用于记录对于数据库的一些学习&#xff0c;有基础也有进阶&#xff0c;有MySQL也有Oracle 行列转换 • Oracle版 oracle的行列转换前言1.数据…

免费开源 | 基于SpringBoot的博客系统

介绍 基于springboot后端架构&#xff0c;websocket实现私信&#xff0c;前端采用thymeleafbootstraplayuiRedis 注册使用邮箱验证注册&#xff0c;且验证码存在redis中&#xff0c;所以需要有redis环境 软件架构 springbootwebsocketthymeleafbootstraplayuiRedismysql 8.…

vue项目业务实现,视频监控-文件流,大屏适配方案(v-scale-screen),websocket前端

最近把以前的业务场景及解决方案整理了一下&#xff0c;具体实现的工具如下&#xff1a; 监控-视频文件流>video.js videojs-contrib-hls 大屏适配方案> v-scale-screen websocket>sockjs-client webstomp-client 视频监控-文件流 使用方法 下载video插件&#xf…

Jmeter性能优化方案

最近用jmeter测试并发出现了访问端口异常问题的排查及解决方案做一个归纳&#xff1a; 背景&#xff1a;接口压测异常情况发生率达到了99% 线上情况&#xff1a; 错误情况展示&#xff1a; 原因&#xff1a; Jmeter里的http sample勾选了keep alive&#xff0c;导致会话一直…

系统上线前,SQL脚本的9大坑

前言 系统上线时&#xff0c;非常容易出问题。 即使之前在测试环境&#xff0c;已经执行过SQL脚本了。但是有时候&#xff0c;在系统上线时&#xff0c;在生产环境执行相同的SQL脚本&#xff0c;还是有可能出现一些问题。 有些小公司&#xff0c;SQL脚本是开发自己执行的&am…

第47步 深度学习图像识别:SqueezeNet建模(Pytorch)

基于WIN10的64位系统演示 一、写在前面 &#xff08;1&#xff09;SqueezeNet SqueezeNet是一种轻量级的深度神经网络架构&#xff0c;由Iandola等人在2016年提出。这种模型的最大特点是参数量极少&#xff0c;仅有510千个参数&#xff0c;而且模型大小只有5MB&#xff0c;比…

MobileNeRF在Windows上的配置

MobileNeRF于2023年提出&#xff0c;源码地址&#xff1a;https://github.com/google-research/jax3d/tree/main/jax3d/projects/mobilenerf &#xff0c;论文为&#xff1a;《MobileNeRF: Exploiting the Polygon Rasterization Pipeline for Efficient Neural Field Renderin…

步入JAVA——环境搭建与项目通览

前言 在这篇文章中&#xff0c;荔枝会介绍如何配置Java后端开发环境并借助一个Java web项目简单介绍一下Java的后端开发逻辑。与python的后端开发逻辑是类似的&#xff0c;Java的后端开发其实也是通过一个个类来实现的。对于像荔枝这种小白白来说&#xff0c;入门的第一个练手J…

GitHub快速上手--GitHub高效操作教程

一、前言 如果你正在看我的这篇文章&#xff0c;说明你已经对GitHub有了一些基础的了解&#xff0c;下面我们将详细叙述每一步的操作&#xff0c;以保证你能够快速上手GitHub&#xff0c;完成对代码的管理。 二、创建仓库 登录GitHub账号&#xff0c;点击页面右上角的加号&am…

idea创建web项目没有jsp选项,不识别jsp,没有tomcat选项

如果你的idea的web项目中没有jsp选项 同时也不识别jsp 那么建议你检查一下你的idea是否为社区版 如果是社区版那么没有jsp的问题无法解决&#xff0c;这只是无法识别&#xff0c;但是语句对的可以正常运行 解决这个问题建议换个idea 至于tomcat&#xff0c; 在plugins中搜s…