AOP切面编程,以及自定义注解实现切面

AOP切面编程

  1. 通知类型
  2. 表达式
  3. 重用表达式
  4. 切面优先级
  5. 使用注解开发,加上注解实现某些功能

简介

  • 动态代理分为JDK动态代理和cglib动态代理
  • 当目标类有接口的情况使用JDK动态代理和cglib动态代理,没有接口时只能使用cglib动态代理
  • JDK动态代理动态生成的代理类会在com.sun.proxy包下,类名为$proxy1,和目标类实现相同的接口
  • cglib动态代理动态生成的代理类会和目标在在相同的包下,会继承目标类
  • 动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口(兄弟两个拜把子模式)。
  • cglib:通过继承被代理的目标类(认干爹模式)实现代理,所以不需要目标类实现接口。
  • AspectJ:是AOP思想的一种实现。本质上是静态代理,将代理逻辑“织入”被代理的目标类编译得到的字节码文件,所以最终效果是动态的。weaver就是织入器。Spring只是借用了AspectJ中的注解。

依赖

<!--spring context依赖-->
<!--当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.0.2</version>
</dependency><!--spring aop依赖-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>6.0.2</version>
</dependency>
<!--spring aspects依赖-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>6.0.2</version>
</dependency>

注解说明

  • @Aspect表示这个类是一个切面类
  • @Component注解保证这个切面类能够放入IOC容器

简单使用

几种通知类型

  • 前置通知@Before()
  • 环绕通知@Around()
  • 返回通知@AfterReturning()
  • 异常通知@AfterThrowing()
  • 后置通知@After()

表达式介绍

在这里插入图片描述

切入点表达式

在方法中可以传入参数public void afterMethod(JoinPoint joinPoint),类型为JoinPoint

  • 获取连接点的签名信息

    • String methodName = joinPoint.getSignature().getName()
      
  • 获取目标方法的返回值

    • 其中表达式中returning后面参数result要与public void afterReturningMethod(JoinPoint joinPoint, Object result)传入参数名称一样,类型可以不一样但是名称要一样。

    • @AfterReturning(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", returning = "result")
      public void afterReturningMethod(JoinPoint joinPoint, Object result){String methodName = joinPoint.getSignature().getName();System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result);
      }
      
  • 获取目标方法的异常

    • 在表达式中加入throwing = "ex",和上面一样,传入参数名称要一直,也要是ex,如:Throwable ex

    • @AfterThrowing(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", throwing = "ex")
      public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){String methodName = joinPoint.getSignature().getName();System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
      }
      
切入点表达式使用

前置通知

@Aspect// 表示这个类是一个切面类
@Component// 注解保证这个切面类能够放入IOC容器
public class LogAspect {// 设置切入点和通知类型@Before(value = "execution(public int com.example.aop.annoaop.CalculatorImpl.*(..))")public void beforeMethod(JoinPoint joinPoint) {Object[] args = joinPoint.getArgs();System.out.println("Logger-->前置通知" + "参数:" + args[0] + "," + args[1]);}
}

后置通知

@Aspect// 表示这个类是一个切面类
@Component// 注解保证这个切面类能够放入IOC容器
public class LogAspect {@After(value = "execution(public int com.example.aop.annoaop.CalculatorImpl.*(..))")public void afterMethod(JoinPoint joinPoint) {String name = joinPoint.getSignature().getName();// 方法名System.out.println("Logger-->后置通知,方法名:" + name);}
}

返回通知

@Aspect// 表示这个类是一个切面类
@Component// 注解保证这个切面类能够放入IOC容器
public class LogAspect {@AfterReturning(value = "execution(public int com.example.aop.annoaop.CalculatorImpl.*(..))", returning = "result")public void afterReturn(JoinPoint joinPoint, Object result) {String methodName = joinPoint.getSignature().getName();System.out.println("Logger-->返回通知,方法名:" + methodName + ",结果:" + result);}
}

异常通知

@Aspect// 表示这个类是一个切面类
@Component// 注解保证这个切面类能够放入IOC容器
public class LogAspect {@AfterThrowing(value = "execution(public int com.example.aop.annoaop.CalculatorImpl.*(..))", throwing = "ex")public void afterThrowingMethod(JoinPoint joinPoint, Exception ex) {String methodName = joinPoint.getSignature().getName();System.out.println("Logger-->异常通知,方法名:" + methodName + ",异常:" + ex);}
}

环绕通知

@Aspect// 表示这个类是一个切面类
@Component// 注解保证这个切面类能够放入IOC容器
public class LogAspect {@Around(value = "execution(public int com.example.aop.annoaop.CalculatorImpl.*(..))")public Object aroundMethod(ProceedingJoinPoint joinPoint) {String methodName = joinPoint.getSignature().getName();String args = Arrays.toString(joinPoint.getArgs());Object result = null;try {System.out.println("环绕通知-->目标对象方法执行之前");// 目标对象(连接点)方法的执行result = joinPoint.proceed();System.out.println("环绕通知-->目标对象方法返回值之后");} catch (Throwable throwable) {throwable.printStackTrace();System.out.println("环绕通知-->目标对象方法出现异常时");} finally {System.out.println("环绕通知-->目标对象方法执行完毕");}return result;}
}
重用切入点表达式

申明表达式

@Pointcut("execution(* com.atguigu.aop.annotation.*.*(..))")
public void pointCut(){}

在方法中使用

@Before("pointCut()")
public void beforeMethod(JoinPoint joinPoint){String methodName = joinPoint.getSignature().getName();String args = Arrays.toString(joinPoint.getArgs());System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}

在不同切面中使用

@Before("com.atguigu.aop.CommonPointCut.pointCut()")
public void beforeMethod(JoinPoint joinPoint){String methodName = joinPoint.getSignature().getName();String args = Arrays.toString(joinPoint.getArgs());System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}

在这里插入图片描述

切面的优先级

相同目标方法上同时存在多个切面时,切面的优先级控制切面的内外嵌套顺序。

  • 优先级高的切面:外面
  • 优先级低的切面:里面

使用@Order注解可以控制切面的优先级:

  • @Order(较小的数):优先级高
  • @Order(较大的数):优先级低

将@Order注解放在切面类上而不是方法上!!!

  • 优先级低的
@Aspect// 表示这个类是一个切面类
@Component// 注解保证这个切面类能够放入IOC容器
@Order(4)
public class LogAspect {@After(value = "pointcut()")public void afterMethod2(JoinPoint joinPoint) {String name = joinPoint.getSignature().getName();// 方法名System.out.println("Logger-->后置通知 111111,方法名:" + name);}
}
  • 优先级高的
@Aspect// 表示这个类是一个切面类
@Component// 注解保证这个切面类能够放入IOC容器
@Order(1)
public class NewLogAspect {@After(value = "com.example.aop.annoaop.LogAspect.pointcut()")public void afterMethod(JoinPoint joinPoint) {String name = joinPoint.getSignature().getName();// 方法名System.out.println("Logger-->后置通知 444444,方法名:" + name);}
}
效果

原先交换顺序后

在这里插入图片描述

没有交换顺序前

在这里插入图片描述

注解使用

使用AOP在方法或者接口上写上某些注解,完成特定方法。

实现思路

  1. 创建@interface
  2. 和上面一样要写Aspect,并且要被spring管理
  3. 在方法或者接口上加上@interface注解

实现示例

  • 创建@interface 并命名为BunnyLog
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
// 加上这个注解打印当前时间
public @interface BunnyLog {}
  • 使用AOP切面,需要注意的是:

    • 如果只是单纯的加上注解,不考虑指定类或者类中方法,完成某些功能,要修改下面切入点

      • @Pointcut(value = "@annotation(com.example.aop.annoaop.BunnyLog)")
        public void bunnyPointcut() {
        }
        
    • 如果想指定某些类或者某些方法下的

      • @Pointcut("execution(* com.example.aop.annoaop.*.*(..)) && @annotation(com.example.aop.annoaop.BunnyLog)")
        public void bunnyPointcut() {
        }
        
  • 创建切面

@Aspect
@Component
public class BunnyAspect {/*** 切入点,并且加上了 @BunnyLog注解*/@Pointcut(value = "execution(* com.example.aop.annoaop.*.*(..)) || @annotation(com.example.aop.annoaop.BunnyLog)")public void bunnyPointcut() {}@Before("bunnyPointcut()")public void before(JoinPoint joinPoint) {String name = joinPoint.getSignature().getName();System.out.println("------AOP前置通知生效,方法名称------>" + name);LocalDateTime localDateTime = LocalDateTime.now();DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyy年MM月dd日 HH:mm:ss");String format = localDateTime.format(timeFormatter);System.out.println("BunnyAspect,现在时间====>" + format);}
}
  • 普通方法,需要在类上加上@Component被spring管理,之后再方法中加上注解@BunnyLog
@Component
public class BunnyTestImpl {@BunnyLogpublic void method() {LocalDateTime localDateTime = LocalDateTime.now();DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyy年MM月dd日 HH:mm:ss");String format = localDateTime.format(timeFormatter);System.out.println("测试方法,现在时间====>" + format);}
}
  • 创建一个测试类
public class TestBefore {@Testpublic void test1() {ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");BunnyTestImpl bean = context.getBean(BunnyTestImpl.class);bean.method();}
}

执行结果:

r);
System.out.println(“测试方法,现在时间====>” + format);
}
}


- 创建一个测试类```java
public class TestBefore {@Testpublic void test1() {ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");BunnyTestImpl bean = context.getBean(BunnyTestImpl.class);bean.method();}
}

执行结果:

在这里插入图片描述

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

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

相关文章

【滑动窗口】力扣239.滑动窗口最大值

前面的文章我们练习数十道 动态规划 的题目。相信小伙伴们对于动态规划的题目已经写的 得心应手 了。 还没看过的小伙伴赶快关注一下&#xff0c;学习如何 秒杀动态规划 吧&#xff01; 接下来我们开启一个新的篇章 —— 「滑动窗口」。 滑动窗口 滑动窗口 是一种基于 双指…

03.axios数据提交和错误处理

一.axios常用请求方法和数据提交 1. 想要提交数据&#xff0c;先来了解什么是请求方法 请求方法是一些固定单词的英文&#xff0c;例如&#xff1a;GET&#xff0c;POST&#xff0c;PUT&#xff0c;DELETE&#xff0c;PATCH&#xff08;这些都是http协议规定的&#xff09;&am…

axios的详细使用

目录 axios&#xff1a;现代前端开发的HTTP客户端王者 一、axios简介 二、axios的基本用法 1. 安装axios 2. 发起GET请求 3. 发起POST请求 三、axios的高级特性 1. 拦截器 2. 取消请求 3. 自动转换JSON数据 四、axios在前端开发中的应用 五、总结 axios&#xff1a…

vue中性能优化

目录 1. 编码优化 2. 源码优化 3. 打包优化 4. 利用 Vue Devtools 总结 Vue.js 作为一个强大的前端框架&#xff0c;提供了丰富的功能和工具来帮助开发者构建高效的 Web 应用。然而&#xff0c;在开发过程中&#xff0c;性能优化仍然是一个需要关注的问题。以下是对 Vue.j…

3/7—21. 合并两个有序链表

代码实现&#xff1a; 方法1&#xff1a;递归 ---->难点 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ struct ListNode* mergeTwoLists(struct ListNode *list1, struct ListNode *list2) {/*1.如果l1为…

Luajit 2023移动版本编译 v2.1.ROLLING

文章顶部有编好的 2.1.ROLLING 2023/08/21版本源码 Android 64 和 iOS 64 luajit 目前最新的源码tag版本为 v2.1.ROLLING on Aug 21, 2023应该是修正了很多bug, 我是出现下面问题才编的. cocos2dx-lua 游戏 黑屏 并报错: [LUA ERROR] bad light userdata pointer 编…

空间复杂度的OJ练习——轮转数组

旋转数组OJ链接&#xff1a;https://leetcode-cn.com/problems/rotate-array/ 题目&#xff1a; 思路&#xff1a; 通过题目我们可以知道这是一个无序数组&#xff0c;只需要将数组中的数按给定条件重新排列&#xff0c;因此我们可以想到以下几种方法&#xff1a; 1.暴力求解法…

详解DNS服务

华子目录 概述产生原因作用连接方式 因特网的域名结构拓扑分类域名服务器类型划分 DNS域名解析过程分类解析图图过程分析注意 搭建DNS域名解析服务器概述安装软件bind服务中的三个关键文件 配置文件分析主配置文件共4部分组成区域配置文件作用区域配置文件示例分析正向解析反向…

Linux 之七:Linux 防火墙 和进程管理

防火墙 查看防火墙 查看 Centos7 的防火墙的状态 sudo systemctl status firewalld。 查看后&#xff0c;看到active(running)就意味着防火墙打开了。 关闭防火墙&#xff0c;命令为&#xff1a; sudo systemctl stop firewalld。 关闭后查看是否关闭成功&#xff0c;如果…

js【详解】async await

为什么要使用 async await async await 实现了使用同步的语法实现异步&#xff0c;不再需要借助回调函数&#xff0c;让代码更加易于理解和维护。 (async function () {// await 必须放在 async 函数中try {// 加载第一张图片const img1 await loadImg1()// 加载第二张图片co…

第一代高通S7和S7 Pro音频平台:超旗舰性能,全面革新音频体验

以下文章来源于高通中国 如今&#xff0c;音频内容与形式日渐丰富&#xff0c;可满足人们放松心情、提升自我、获取资讯等需求。得益于手机、手表、耳机、车载音箱等智能设备的广泛应用&#xff0c;音频内容可以更快速触达用户。从《音频产品使用现状调研报告2023》中发现&…

14 OpenCv边缘处理

文章目录 卷积边界问题边缘处理copyMakeBorder 算子代码 卷积边界问题 图像卷积的时候边界像素&#xff0c;不能被卷积操作&#xff0c;原因在于边界像素没有完全跟kernel重叠&#xff0c;所以当3x3滤波时候有1个像素的边缘没有被处理&#xff0c;5x5滤波的时候有2个像素的边缘…

关于 JVM

1、请你谈谈你对JVM的理解&#xff1f; JVM由JVM运行时数据区&#xff08;图示中蓝色框包含部分&#xff09;、执行引擎、本地库接口、本地方法库组成。 JVM运行时数据区&#xff0c;分为方法区、堆、虚拟机栈、本地方法栈和程序计数器。 1.方法区 Java 虚拟机规范中定…

实验一:华为VRP系统的基本操作

1.1实验介绍 1.1.1关于本实验 本实验通过配置华为设备&#xff0c;了解并熟悉华为VRP系统的基本操作 1.1.2实验目的 理解命令行视图的含义以及进入离开命令行视图的方法 掌握一些常见的命令 掌握命令行在线帮助的方法 掌握如何撤销命令 掌握如何使用命令快捷键 1.1.3实验组网 …

将Xilinx DDR3 MIG IP核的APP接口封装成FIFO接口(含源码)

1、概括 前文完成了xilinx DDR3 MIG IP的仿真和上板测试&#xff0c;对MIG IP的读、写需要去通过使能信号和应答信号进行握手。这对于图像处理、AD采集等大量数据的存储不太方便&#xff0c;常见的使用方式是把MIG IP的用户接口封装成FIFO的接口。 如下图所示&#xff0c;如果要…

深入浅出计算机网络 day.1 概论③ 电路交换、分组交换和报文交换

人无法同时拥有青春和对青春的感受 —— 04.3.9 内容概述 01.电路交换、分组交换和报文交换 02.三种交换方式的对比 一、电路交换、分组交换和报文交换 1.电路交换 计算机之间的数据传送是突发式的&#xff0c;当使用电路交换来传送计算机数据时&#xff0c;其线路的传输效率一…

万界星空科技MES系统中的车间管理的作用

在了解mes生产管理系统的作用包括哪些方面之前&#xff0c;我们先来了解一下作为生产管理信息化的关键部分&#xff0c;车间管理系统包含哪几个部分&#xff1a;一、mes系统中的车间管理通常包含以下部分&#xff1a; 1、设备管理&#xff1a;用于监控车间内的设备状态&#xf…

C语言:编译和链接(从.c文件到输出结果的过程)

和黛玉学编程.......> 前言 在ANSI C中&#xff0c;有两个不同的环境 1.翻译环境 2.执行环境 我们在打开编程软件的时候&#xff0c;需要在源文件上添加 如果是C语言&#xff0c;需要使用.C的源文件&#xff0c;是C的话&#xff0c;就是.cpp&#xff0c; 我们创建的.c文件…

复盘-PPT

调整PPT编号起始页码在设计→幻灯片大小 设置所有以及文本项目符号 ## 打开母版&#xff0c;找到对应级别设置重置 当自动生成的smartart图形不符合预期时

【C++】二叉树进阶之二叉搜索树

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;熟练掌握二叉搜索树&#xff0c;能自己模拟实现二…