飞道的博客

Spring(九)AOP

426人阅读  评论(0)

一、AOP 简介

1. 什么是 AOP

AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构。

1. AOP 的作用

AOP是在不改原有代码的前提下对其进行增强。

3. AOP 核心概念

(1)连接点(JoinPoint):程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等。在SpringAOP中,理解为方法的执行。

(2)切入点(Pointcut):匹配连接点的式子。在 SpringAOP 中,一个切入点可以只描述一个具体方法,也可以匹配多个方法。

1️⃣一个具体方法:【com.itheima.dao】包下的 BookDao 接口中的无形参无返回值的 save 方法

2️⃣匹配多个方法:所有的 save 方法,所有的 get 开头的方法,所有以 Dao 结尾的接口中的任意方法,所有带有一个参数的方法

(3)通知(Advice):在切入点处执行的操作,也就是共性功能。在 SpringAOP 中,功能最终以方法的形式呈现

(4)通知类:定义通知的类

(5)切面(Aspect):描述通知与切入点的对应关系

二、AOP入门案例

1. 思路分析

(1)导入坐标(pom.xml)
(2)制作连接点方法(原始操作,Dao接口与实现类)
(3)制作共性功能(通知类与通知)
(4)定义切入点
(5)绑定切入点与通知关系

2. 实现步骤

(1)添加依赖


   
  1. <dependency>
  2. <groupId>org.aspectj </groupId>
  3. <artifactId>aspectjweaver </artifactId>
  4. <version>1.9.4 </version>
  5. </dependency>

(2)定义接口与实现类

(3)定义通知类和通知

通知就是将共性功能抽取出来后形成的方法,共性功能指的就是当前系统时间的打印。
(4)定义切入点
说明:切入点定义依托一个不具有实际意义的方法进行,即无参数、无返回值、方法体无实际逻辑。
(5)制作切面
切面是用来描述通知和切入点之间的关系。绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行位置
(6)将通知类配给容器并标识其为切面类

      
  1. @Component
  2. @Aspect
  3. public class MyAdvice {
  4. @Pointcut("execution(void com.itheima.dao.BookDao.update())")
  5. private void pt () {
  6. }
  7. @Before("pt()")
  8. public void method () {
  9. System.out.println(System.currentTimeMillis());
  10. }
  11. }

三、AOP 工作流程

1. AOP 工作流程

(1)Spring 容器启动

(2)读取所有切面配置中的切入点

(3)初始化bean,判定 bean 对应的类中的方法是否匹配到任意切入点

1️⃣匹配失败,创建对象
2️⃣匹配成功,创建原始对象(目标对象)的代理对象

(4)获取bean执行方法

1️⃣获取bean,调用方法并执行,完成操作
2️⃣获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作

2. AOP核心概念

(1)目标对象(Target):原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的
(2)代理(Proxy):目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现

四、AOP 配置管理

1. AOP切入点表达式

切入点: 要进行增强的方法
切入点表达式: 要进行增强的方法的描述方式

1.1 语法格式

对于切入点的描述:
(1)描述方式一:执行com.itheima.dao包下的  BookDao  接口中的无参数  update  方法
execution(void com.itheima.dao.BookDao.update())

(2)描述方式二:执行com.itheima.dao.impl包下的 BookDaoImpl 类中的无参数 update 方法

execution(void com.itheima.dao.impl.BookDaoImpl.update())
切入点表达式 标准格式 :动作关键字 ( 访问修饰符 返回值 包名 . / 接口名 . 方法名 ( 参数 ) 异常
名)

 

1.2 通配符

使用通配符描述切入点,主要的目的就是简化之前的配置

 

1.3 书写技巧

2. AOP通知类型

AOP 通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置。
共提供了 5 种通知类型:
(1)前置通知
(2)后置通知
(3) 环绕通知 ( 重点 )
(4)返回后通知 ( 了解 )
(5)抛出异常后通知 ( 了解 )

 

通知类:

     
  1. @Component
  2. @Aspect
  3. public class MyAdvice {
  4. @Pointcut("execution(void com.itheima.dao.BookDao.update())")
  5. private void pt () {
  6. }
  7. @Before("pt()")
  8. public void before () {
  9. System.out.println( "before advice ...");
  10. }
  11. @After("pt()")
  12. public void after () {
  13. System.out.println( "after advice ...");
  14. }
  15. @Around("pt()")
  16. public Object around (ProceedingJoinPoint pjp) throws Throwable {
  17. //获取执行签名信息
  18. Signature signature = pjp.getSignature();
  19. //通过签名获取执行类型(接口名)
  20. String className = signature.getDeclaringTypeName();
  21. String methodName = signature.getName();
  22. System.out.println( "around before advice ...");
  23. //通过签名获取执行操作名称(方法名)
  24. Object ret = pjp.proceed();
  25. System.out.println( "around after advice ...");
  26. return ret;
  27. }
  28. @AfterReturning("pt()")
  29. public void afterReturning () {
  30. System.out.println( "afterReturning advice ...");
  31. }
  32. @AfterThrowing("pt()")
  33. public void afterThrowing () {
  34. System.out.println( "afterThrowing advice ...");
  35. }
  36. }

 环绕通知注意事项

(1) 环绕通知必须依赖形参  ProceedingJoinPoint  才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知
(2)通知中如果未使用  ProceedingJoinPoint  对原始方法进行调用将跳过原始方法的执行
(3)对原始方法的调用可以不接收返回值,通知方法设置成 void 即可,如果接收返回值,最好设定为 Object 类型
(5) 原始方法的返回值如果是  void  类型,通知方法的返回值类型可以设置成  void, 也可以设置成 Object
(5) 由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须要处理  Throwable  异常

3. AOP通知获取数据

(1)获取切入点方法的参数,所有的通知类型都可以获取参数
1️⃣JoinPoint :适用于前置、后置、返回后、抛出异常后通知
2️⃣ProceedingJoinPoint :适用于环绕通知
(2)获取切入点方法返回值,前置和抛出异常后通知是没有返回值,后置通知可有可无,所以不做研究
1️⃣返回后通知
2️⃣环绕通知
(3)获取切入点方法运行异常信息,前置和返回后通知是不会有,后置通知可有可无,所以不做研究
1️⃣抛出异常后通知
2️⃣环绕通知
编写通知类:

    
  1. @Component
  2. @Aspect
  3. public class MyAdvice {
  4. @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
  5. private void pt () {
  6. }
  7. @Before("pt()")
  8. public void before (JoinPoint jp) {
  9. Object[] args = jp.getArgs();
  10. System.out.println(Arrays.toString(args));
  11. System.out.println( "before advice ...");
  12. }
  13. @After("pt()")
  14. public void after (JoinPoint jp) {
  15. Object[] args = jp.getArgs();
  16. System.out.println(Arrays.toString(args));
  17. System.out.println( "after advice ...");
  18. }
  19. @Around("pt()")
  20. public Object around (ProceedingJoinPoint pjp) throws Throwable {
  21. Object[] args = pjp.getArgs();
  22. System.out.println(Arrays.toString(args));
  23. System.out.println( "around before advice ...");
  24. //通过签名获取执行操作名称(方法名)
  25. Object ret = pjp.proceed(args);
  26. System.out.println( "around after advice ...");
  27. return ret;
  28. }
  29. @AfterReturning(value = "pt()", returning = "ret")
  30. public void afterReturning (Object ret) {
  31. //ret是返回值
  32. System.out.println( "afterReturning advice ..." + ret);
  33. }
  34. @AfterThrowing(value = "pt()", throwing = "t")
  35. public void afterThrowing (Throwable t) {
  36. System.out.println( "afterThrowing advice ..." + t);
  37. }
  38. }

转载:https://blog.csdn.net/yirenyuan/article/details/127744481
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场