AOP讲解and小demo
创始人
2025-05-28 02:45:19
0

AOP

1.1 AOP理解

OOP (Object Oriented Programming) 面向对象编程

AOP (Aspect Oritented Programming) 面向切面编程)

两者不是替代的关系而是互补的,AOP主要就是在不改变原本代码功能的情况下去新增功能

在spring中AOP很重要,可以理解为,AB业务互不影响且要去实现C的业务,但是我们需要在执行A业务的时候,在C业务功能的基础上,让A业务完成想要的功能,让B业务完成B业务需要的更强大的功能,前提就是C业务是基础业务是不能被更改的,不然就要影响其他业务。

所以在这个时候,AOP就做出了一个动作,A业务在C业务的想要添加的方法前面砍断,添加一个新的A自己的业务上去后,再将重新组合好的业务给织入原来的C业务方法那里去,继续执行C业务后面的功能。B业务一样的。

1.2 AOP实现

1.2.1 注解实现

  1. 添加依赖

org.springframeworkspring-aop5.3.18


org.aspectjaspectjrt1.9.6runtime


org.springframeworkspring-aspects5.3.18
  1. 修改xml配置文件






最主要的就是加上了:

xmlns:aop="http://www.springframework.org/schema/aop"http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xs

注意:使用AOP的话是需要开启动态代理的哈,proxy-target-class的值true代表jdk实现,false代表cglib实现

  1. 创建切面类
    创建切面类,将其放入spring中,我们这里来通过测试类中testJDK方法来演示,在UserServiceImpl中的public User login(String username, String password) { }这里进行切入:先看一下未切入之前的输出结果:
    在这里插入图片描述

开始创建切入类

//放入spring中
@Component
@Aspect //这个标签是标记这个类是一个切面
public class PetAspect {/*** 标记再哪里什么时候执行切入* @param joinPoint*///    execution:里面写的就是切入点,切入点有很多种语法,下面这个是最精准的切入点,直接定位到了具体的方法,切入点的语法有很多,下面一一介绍@Before("execution(public com.xiaowang.entity.User com.xiaowang.PetService.impl.PetServiceImpl.login(java.lang.String,java.lang.String))")//想将下面这个方法切入到测试类中testJDK()方法的前面执行public void before(JoinPoint joinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行//切入面中,是可以获取很多被拦截的东西,System.out.println("before之前执行");System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs()));System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName());System.out.println("拦截的位置为:"+joinPoint.getStaticPart());System.out.println("代理对象为:"+joinPoint.getThis());System.out.println("目标对象为:"+joinPoint.getTarget());}
}

执行结果:
在这里插入图片描述
我们可以看到,就在login方法的前面就添加进去了我们的对象的方法,我们拦截的东西也可以看到了,这里同时还有多种类似于@before的注解,其功能就是在这个方法切入点的哪一个位置进行切入,例如还有:

/*** 标记再哪里什么时候执行切入,* @befor 代表在标记位置的前面切入* @param joinPoint*///    execution:里面写的就是切入点,切入点有很多种语法,下面这个是最精准的切入点,直接定位到了具体的方法,切入点的语法有很多,下面一一介绍@Before("execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))")//想将下面这个方法切入到测试类中testJDK()方法的前面执行public void before(JoinPoint joinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行//切入面中,是可以获取很多被拦截的东西,System.out.println("before之前执行");System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs()));System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName());System.out.println("拦截的位置为:"+joinPoint.getStaticPart());System.out.println("代理对象为:"+joinPoint.getThis());System.out.println("目标对象为:"+joinPoint.getTarget());}/*** @After 在切入点的后面切入方法功能进去* 相当于finally执行,就是必备执行* @param joinPoint*/@After("execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))")//想将下面这个方法切入到测试类中testJDK()方法的前面执行public void after(JoinPoint joinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行//切入面中,是可以获取很多被拦截的东西,System.out.println("after执行");System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs()));System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName());System.out.println("拦截的位置为:"+joinPoint.getStaticPart());System.out.println("代理对象为:"+joinPoint.getThis());System.out.println("目标对象为:"+joinPoint.getTarget());}/*** @AfterReturning 相当于正常执行,就是拦截方法正确执行后就可以执行* @param joinPoint*/@AfterReturning("execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))")//想将下面这个方法切入到测试类中testJDK()方法的前面执行public void afterReturning(JoinPoint joinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行//切入面中,是可以获取很多被拦截的东西,System.out.println("afterReturning执行");System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs()));System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName());System.out.println("拦截的位置为:"+joinPoint.getStaticPart());System.out.println("代理对象为:"+joinPoint.getThis());System.out.println("目标对象为:"+joinPoint.getTarget());}/*** 拦截的语句那里抛出异常后被执行* @param joinPoint*/@AfterThrowing("execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))")//想将下面这个方法切入到测试类中testJDK()方法的前面执行public void afterThrowing(JoinPoint joinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行//切入面中,是可以获取很多被拦截的东西,System.out.println("afterThrowing执行");System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs()));System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName());System.out.println("拦截的位置为:"+joinPoint.getStaticPart());System.out.println("代理对象为:"+joinPoint.getThis());System.out.println("目标对象为:"+joinPoint.getTarget());}

①拦截方法正常执行的时候结果:
在这里插入图片描述
②拦截方法有异常的时候结果:
在这里插入图片描述

如果使用AOP去管理事务的话,那我们就可以在before里面写事务的开启,在afterReturning中提交事务,在afterThrowing中去回滚事务,

所以在@afterThrowing这里的话就可以再加一点属性,去设置哪些地方需要回滚,哪些地方不需要回滚

/*** 拦截的语句那里抛出异常后被执行,相当于catch* @param joinPoint*/@AfterThrowing(value = "execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))",throwing = "e")//想将下面这个方法切入到测试类中testJDK()方法的前面执行public void afterThrowing(JoinPoint joinPoint,Exception e){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行//切入面中,是可以获取很多被拦截的东西,System.out.println("afterThrowing执行");System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs()));System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName());System.out.println("拦截的位置为:"+joinPoint.getStaticPart());System.out.println("代理对象为:"+joinPoint.getThis());System.out.println("目标对象为:"+joinPoint.getTarget());//事务System.out.println("抛出异常进行回滚"+e.getMessage());}

在这里插入图片描述
独特的环绕执行:

/*** 环绕执行,就像过滤器一样,再dofilt前面执行,后面执行* @param proceedingJoinPoint* @return*/@Around(value = "execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))")//想将下面这个方法切入到测试类中testJDK()方法的前面执行public Object around(ProceedingJoinPoint proceedingJoinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行//切入面中,是可以获取很多被拦截的东西,System.out.println("around之前执行");Object proceed = proceedingJoinPoint.proceed();//代理的方法执行System.out.println("around之后执行");return proceed;}

1.2.2 xml实现

使用xml实现当然前期的依赖呀、配置文件的头部那些基本的都是要有哈,和注解实现的前面一样,区别就在于对切面类的编写的时候,切入点不用写在注解上了,而是写在xml文件里了,也就是删掉or注释掉切面类中的注解呀


//放入spring中
@Component
@Aspect //这个标签是标记这个类是一个切面
public class PetAspect {/*** 标记再哪里什么时候执行切入,* @befor 代表在标记位置的前面切入* @param joinPoint*///    execution:里面写的就是切入点,切入点有很多种语法,下面这个是最精准的切入点,直接定位到了具体的方法,切入点的语法有很多,下面一一介绍//  @Before("execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))")//想将下面这个方法切入到测试类中testJDK()方法的前面执行public void before(JoinPoint joinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行//切入面中,是可以获取很多被拦截的东西,System.out.println("before之前执行");System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs()));System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName());System.out.println("拦截的位置为:"+joinPoint.getStaticPart());System.out.println("代理对象为:"+joinPoint.getThis());System.out.println("目标对象为:"+joinPoint.getTarget());}/*** @After 在切入点的后面切入方法功能进去* 相当于finally执行,就是必备执行* @param joinPoint*///  @After("execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))")//想将下面这个方法切入到测试类中testJDK()方法的前面执行public void after(JoinPoint joinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行//切入面中,是可以获取很多被拦截的东西,System.out.println("after执行");System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs()));System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName());System.out.println("拦截的位置为:"+joinPoint.getStaticPart());System.out.println("代理对象为:"+joinPoint.getThis());System.out.println("目标对象为:"+joinPoint.getTarget());}/*** @AfterReturning 相当于正常执行,就是拦截方法正确执行后就可以执行* @param joinPoint*///  @AfterReturning("execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))")//想将下面这个方法切入到测试类中testJDK()方法的前面执行public void afterReturning(JoinPoint joinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行//切入面中,是可以获取很多被拦截的东西,System.out.println("afterReturning执行");System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs()));System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName());System.out.println("拦截的位置为:"+joinPoint.getStaticPart());System.out.println("代理对象为:"+joinPoint.getThis());System.out.println("目标对象为:"+joinPoint.getTarget());}/*** 拦截的语句那里抛出异常后被执行,相当于catch* @param joinPoint*///  @AfterThrowing(value = "execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))",throwing = "e")//想将下面这个方法切入到测试类中testJDK()方法的前面执行public void afterThrowing(JoinPoint joinPoint,Exception e){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行//切入面中,是可以获取很多被拦截的东西,System.out.println("afterThrowing执行");System.out.println("拦截的参数为:"+ Arrays.toString(joinPoint.getArgs()));System.out.println("拦截的方法为:"+joinPoint.getSignature().getDeclaringTypeName() + joinPoint.getSignature().getName());System.out.println("拦截的位置为:"+joinPoint.getStaticPart());System.out.println("代理对象为:"+joinPoint.getThis());System.out.println("目标对象为:"+joinPoint.getTarget());//事务System.out.println("抛出异常进行回滚"+e.getMessage());}/*  *//*** 环绕执行,就像过滤器一样,再dofilt前面执行,后面执行* @param proceedingJoinPoint* @return*//*//@Around(value = "execution(public com.xiaowang.entity.Pet com.xiaowang.dao.impl.PetDaoImpl.add(java.lang.String,java.lang.String))")//想将下面这个方法切入到测试类中testJDK()方法的前面执行public Object around(ProceedingJoinPoint proceedingJoinPoint){//JoinPoint参数是切入点的意思,具体的切入点通过before注解去填写就行//切入面中,是可以获取很多被拦截的东西,System.out.println("around之前执行");Object proceed = proceedingJoinPoint.proceed();//代理的方法执行System.out.println("around之后执行");return proceed;}
*/
}

注意:我们在切面类上面加了@Aspect 这个注解后,就代表着这是一个切面了,就不用在xml中去通过去定义这个类未切面类,两种方法二选一即可。

xml配置文件:




 






1.2.3 切入点语法

直接精准到一个方法上面去
execution( public com.zlt.entity.User com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String))
任意权限修饰符
execution(  com.zlt.entity.User com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String))
无返回类型
execution( void com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String))
有返回类型
execution( !void com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String))
任意返回类型
execution( * com.zlt.service.impl.UserServiceImpl.login(java.lang.String,java.lang.String))
任意参数
execution( * com.zlt.service.impl.UserServiceImpl.login(..))
类中的任意方法
execution( * com.zlt.service.impl.UserServiceImpl.*(..))
类中以指定内容开头的方法
execution( * com.zlt.service.impl.UserServiceImpl.select*(..))
包中的任意类的任意方法不包含子包下面的类
execution( * com.zlt.service.impl.*.*(..))
包中及其下的任意类的任意方法
execution( * com.zlt.service..*.*(..))

1.3 AOP应用场景

  1. 事务管理的时候(before里开启事务、afterThowning回滚事务,after提交事务)
  2. 获取某个功能的执行时间(before设置开始时间、after设置结束时间)
  3. …等等

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...