动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

二、AOP面向切面编程官方下载地址:动力节点官网视频观看地址https://www.bilibili.com/video/BV1nz4y1d7uy
2.1 概述AOP(Aspect Orient Programming) 。面向切面编程是从动态角度考虑程序运行过程

  • AOP 底层,就是采用动态代理模式实现的 。采用了两种代理:JDK 的动态代理,与 CGLIB的动态代理,AOP就是动态代理的规范化,把动态代理的实现步骤,方式都定义好了,让开发人员用一种统一的方式,使用动态代理
  • Aspect: 切面,给你的目标类增加的功能,就是切面 。像上面用的日志,事务都是切面 。切面的特点:一般都是非业务方法,独立使用的
  • Orient:面向,对着
  • Programming:编程
2.2 相关术语1. Aspect:切面,表示增强的功能,就是一堆代码,完成某个一个功能 。非业务功能,常见的切面功能有日志,事务,统计信息,参数检查,权限验证,切面用于组织多个Advice,Advice放在切面中定义,实际就是对主业务逻辑的一种增强
2. JoinPoint:连接点,连接业务方法和切面的位置 。就某类中的业务方法,程序执行过程中明确的点,如方法的调用,或者异常的抛出 。在Spring AOP中,连接点总是方法的调用
3. Pointcut:切入点,指多个连接点方法的集合 。多个方法 。可以插入增强处理的连接点 。简而言之,当某个连接点满足指定要求时,该连接点将被添加增强处理,该连接点也就变成了切入点
4. Advice:AOP框架在特定的切入点执行的增强处理 。处理有"around"、"before"和"after"等类型,能表示切面功能执行的时间,切入点定义切入的位置,通知定义切入的时间
5. Target:目标对象,目 标 对 象 指 将 要 被 增 强 的 对 象。即 包 含 主 业 务 逻 辑 的 类 的 对 象
2.3 AspectJ2.3.1 概述AspectJ是一个基于Java语言的AOP框架,提供了强大的AOP功能,其主要包括两个部分:
  • 一个部分定义了如何表达、定义AOP编程中的语法规范;
  • 另一个部分是工具部分,包括编译、调试工具等
aspectJ框架实现aop的两种方式:
  1. 使用xml的配置文件 : 配置全局事务
  2. 使用注解,我们在项目中要做aop功能,一般都使用注解,aspectj有5个注解
  • @Before
  • @AfterReturning
  • @Around
  • @AfterThrowing
  • @After
2.3.2 AspectJ的切入点表达式表达式原型:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

文章插图
相关解释:
  • modifiers-pattern? 访问权限类型
  • ret-type-pattern 返回值类型
  • declaring-type-pattern? 包名类名
  • name-pattern(param-pattern) 方法名(参数类型和参数个数)
  • throws-pattern 抛出异常类型
  • ?表示可选的部分
以上表达式一共4个部分
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
符号意义*0至多个任意字符. .用在方法参数中,表示任意多个参数;用在包名后,表示当前包与子包路径+用在类名后,表示当前类及其子类;用在接口后,表示当前接口及其实现类相关实例:
  • execution(public * *(..)):任意公共方法
  • execution(* set*(..)):任何一个以“set”开始的方法
  • execution(* com.xyz.service.*.*(..)):定义在 service 包里的任意类的任意方法
  • execution(* com.xyz.service..*.*(..)):定义在 service 包或者子包里的任意类的任意方法 。“..”出现在类名中时,后面必须跟“*”,表示包、子包下的所有类
  • execution(* *..service.*.*(..)):指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点
  • execution(* com.xyz.service.IAccountService+.*(..)):IAccountService 若为接口,则为接口中的任意方法及其所有实现类中的任意方法;若为类,则为该类及其子类中的任意方法
  • execution(* joke(String,int))):所有的 joke(String,int)方法,且 joke()方法的第一个参数是 String,第二个参数是 int;如果方法中的参数类型是 java.lang 包下的类,可以直接使用类名,否则必须使用全限定类名,如 joke( java.util.List, int)
2.3.3 前置通知:@Before1.方法的定义要求:
  • 公共方法 public
  • 方法没有返回值
  • 方法名称自定义
  • 方法可以有参数,也可以没有参数
2.@Before: 前置通知注解
属性:value,是切入点表达式,表示切面的功能执行的位置
位置:在方法的上面
1.配置依赖
<!--spring依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.5.RELEASE</version></dependency><!--aspectj依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.2.5.RELEASE</version></dependency>
动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

文章插图
2.创建业务接口与实现类对象
Service.interfacepublic interface Service {public void doSome();}点击并拖拽以移动ServiceImpl.java@org.springframework.stereotype.Service("myService")public class ServiceImpl implements Service {@Overridepublic void doSome() {System.out.println("这是我的业务方法!!!");}}
动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

文章插图
3.创建切面类:MyAspect.java
@Aspect@Component("myAspect")public class MyAspect {/***指定通知方法中的参数:JoinPoint**/@Before(value = "https://tazarkount.com/read/execution(void *..doSome(..))")public void before(){System.out.println("这是前置通知");}}
动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

文章插图
4.配置applicationContext.xml文件
<!--扫描文件--><context:component-scan base-package="com.jjh.*"/><!--声明自动代理生成器--><aop:aspectj-autoproxy/>
动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

文章插图
5.测试类调用
@org.junit.Testpublic void demo01(){String config = "applicationContext.xml";ApplicationContext app = new ClassPathXmlApplicationContext(config);Service proxy = (Service) app.getBean("myService");//输出当前类的信息:com.sun.proxy.$Proxy17//证明其使用的是JDK动态代理System.out.println(proxy.getClass().getName());proxy.doSome();}
动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

文章插图
2.3.4 JoinPoint
  • 指定通知方法中的参数 : JoinPoint
  • JoinPoint:业务方法,要加入切面功能的业务方法
  • 作用:可以在通知方法中获取方法执行时的信息,例如方法名称,方法的实参
  • 如果需要切面功能中方法的信息,就加入JoinPoint
  • JoinPoint参数的值是由框架赋予,必须是第一个位置的参数
  • 不止前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该参数
MyAspect.java
@Aspect@Component("myAspect")public class MyAspect {@Before(value = "https://tazarkount.com/read/execution(void *..doSome(..))")public void before(JoinPoint joinPoint){//获取方法的定义System.out.println("方法的签名(定义):" + joinPoint.getSignature());System.out.println("方法的名称:" + joinPoint.getSignature().getName());//获取方法的实参Object[] args = joinPoint.getArgs();for (Object arg : args) {System.out.println("参数:" + arg);}System.out.println("这是前置通知!!!");}}
动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

文章插图
测试类
@org.junit.Testpublic void demo01(){String config = "applicationContext.xml";ApplicationContext app = new ClassPathXmlApplicationContext(config);Service proxy = (Service) app.getBean("myService");proxy.doSome("张三",20);}
动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

文章插图
打印结果
2.3.5 后置通知:@AfterReturning
  • 在目标方法执行之后执行
  • 由于是目标方法之后执行,所以可以获取到目标方法的返回值
  • 该注解的returning属性就是用于指定接收方法返回值的变量名的
  • 所以,被注解为后置通知的方法,除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量
  • 该变量最好为 Object 类型,因为目标方法的返回值可能是任何类型
业务方法
@Component("myService2")public class SomeServiceImpl implements SomeService {@Overridepublic void doSome() {System.out.println("这是业务方法!!!");}@Overridepublic String doOther(String str, int i) {return "业务方法doOther的返回值!!!";}}
动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

文章插图
后置通知
1.该注解的 returning 属性就是用于指定接收方法返回值的变量名的
2.除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量
3.该变量最好为Object 类型,因为目标方法的返回值可能是任何类型
4.方法的定义要求:
  • 公共方法 public
  • 方法没有返回值
  • 方法名称自定义
  • 方法有参数的,推荐是Object,参数名自定义
5.@AfterReturning:后置通知
  • value:切入点表达式
  • returning:自定义的变量,表示目标方法的返回值的,自定义变量名必须和通知方法的形参名一样
  • 可以根据业务方法的返回值做出相应的操作
@AfterReturning(value = "https://tazarkount.com/read/execution(String *..doOther(..))",returning = "res")public void myAfterReturning(JoinPoint joinPoint,Object res){//获取方法的签属(定义)System.out.println(joinPoint.getSignature());//获取方法的参数Object[] args = joinPoint.getArgs();for (Object arg : args) {System.out.println("目标方法参数:" + arg);}//目标方法的返回值System.out.println(res);//后置通知System.out.println("后置通知!!!");}
动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

文章插图
测试类
@Testpublic void demo02(){String config = "applicationContext.xml";ApplicationContext app = new ClassPathXmlApplicationContext(config);SomeService proxy = (SomeService)app.getBean("myService2");proxy.doOther("Dick",20);}点击并拖拽以移动2.3.6 环绕通知:@Around在目标方法执行之前之后执行 。被注解为环绕增强的方法要有返回值
被注解为环绕增强的方法要有返回值,Object 类型 。并且方法可以包含一个ProceedingJoinPoint类型的参数
【动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程】接口ProceedingJoinPoint其中有一个proceed() 方法,用于执行目标方法
若目标方法有返回值,则该方法的返回值就是目标方法的返回值 。最后,环绕增强方法将其返回值返回 。该增强方法实际是拦截了目标方法的执行
  • 切面类
1. 环绕通知方法的定义格式:
  • public
  • 必须有一个返回值,推荐使用Object
  • 方法名称自定义
  • 方法有参数,固定的参数 ProceedingJoinPoint
2. 特点
  • 在目标方法的前和后都能增强功能
  • 控制目标方法是否被调用执行
  • 修改原来的目标方法的执行结果,影响最后的调用结果
  • 它是功能最强的通知
3.环绕通知等同于jdk动态代理的InvocationHandler接口
4.参数:ProceedingJoinPoint 就等同于 Method,用于执行目标方法
5.返回值: 就是目标方法的执行结果,可以被修改
6.环绕通知:经常做事务,在目标方法之前开启事务,执行目标方法,在目标方法之后提交事务
@Around(value = "https://tazarkount.com/read/execution(String *..do(..))")public Object myAround(ProceedingJoinPoint p) throws Throwable {Object result = null;//前置功能增强System.out.println("前置功能增强!!");//等同于method.invoke(); Object result = doFirst();result = p.proceed();//后置功能增强System.out.println("后置功能增强");return result;}
动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

文章插图
测试类
@Testpublic void demo01(){ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");SomeService proxy = (SomeService)app.getBean("Service3");String result = proxy.doFirst("Dick", 99);//输出返回结果System.out.println(result);}
动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

文章插图
2.3.7 异常通知:@AfterThrowing在目标方法抛出异常后执行
该注解的 throwing 属性用于指定所发生的异常类对象
被注解为异常通知的方法可以包含一个参数 Throwable,参数名称为 throwing 指定的名称,表示发生的异常对象
  • 业务方法
@Overridepublic void doSecond() {System.out.println("执行业务方法doSecond()" + (10/0));}
动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

文章插图
  • 切面类
1. 异常通知方法的定义格式:
  • 访问权限public
  • 没有返回值
  • 方法名称自定义
  • 方法有个一个Exception,也可以使用JoinPoint
2. @AfterThrowing:异常通知
  • 属性:
value 切入点表达式
throwinng 自定义的变量,表示目标方法抛出的异常对象,变量名必须和方法的参数名一样
  • 特点:
在目标方法抛出异常时执行的
可以做异常的监控程序,监控目标方法执行时是不是有异常,如果有异常,可以发送邮件,短信进行通知
@AfterThrowing(value = "https://tazarkount.com/read/execution(* *..SomeServiceImpl.doSecond(..))",throwing = "ex")public void myAfterThrowing(Exception ex) {System.out.println("异常通知:方法发生异常时,执行:"+ex.getMessage());//发送邮件,短信,通知开发人员}
动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

文章插图
2.3.8 最终通知:@After无论目标方法是否抛出异常,该增强均会被执行
  • 业务方法
@Overridepublic void doThird() {System.out.println("执行业务方法doThird()");}
动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

文章插图
切面类
1. 最终通知的定义格式:
  • 访问权限public
  • 没有返回值
  • 方法名称自定义
  • 方法没有参数,但是可以使用JoinPoint
2. @After :最终通知特点
  • 总是会执行
  • 在目标方法之后执行
//等同以下执行方式try{SomeServiceImpl.doThird(..)}catch(Exception e){}finally{myAfter()}点击并拖拽以移动@After(value = "https://tazarkount.com/read/execution(* *..SomeServiceImpl.doThird(..))")publicvoidmyAfter(){System.out.println("执行最终通知,总是会被执行的代码");//一般做资源清除工作的 。}
动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

文章插图
2.3.9 @Pointcut 定义切入点当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦;AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式
将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均可使用该方法名作为切入点
代表的就是@Pointcut定义的切入点 。这个使用@Pointcut注解的方法一般使用 private 的标识方法,即没有实际作用的方法
  • 切面类
1.@Pointcut: 定义和管理切入点,如果你的项目中有多个切入点表达式是重复的,可以复用的 。
2.特点:
当使用@Pointcut定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名
其它的通知中,value属性就可以使用这个方法名称,代替切入点表达式了
@After(value = "https://tazarkount.com/read/mypt()")publicvoidmyAfter(){System.out.println("执行最终通知,总是会被执行的代码");//一般做资源清除工作的 。}@Before(value = "https://tazarkount.com/read/mypt()")publicvoidmyBefore(){System.out.println("前置通知,在目标方法之前先执行的");}@Pointcut(value = "https://tazarkount.com/read/execution(* *..SomeServiceImpl.doThird(..))" )private void mypt(){//无需代码,}
动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

文章插图
2.4 代理方式更换如果目标类有接口,默认使用jdk动态代理,如果目标类没有接口,则使用CGlib动态代理
如果想让具有接口的目标类使用CGlib的代理方式,需要以下配置文件
<aop:aspectj-autoproxy proxy-target-class="true"/>
动力节点springcloud笔记 二 动力节点Spring框架学习笔记-王鹤AOP面向切面编程

文章插图