• Spring(九)AOP


    一、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.aspectjgroupId>
    3. <artifactId>aspectjweaverartifactId>
    4. <version>1.9.4version>
    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. }
  • 相关阅读:
    matplotlib入门详细教程
    AI基础设施Grass零撸空投,最新融资350万美元!
    2023年中国铁路安全行车系统市场规模现状及行业细分市场分析[图]
    Springboot丽水土特产销售网站e80z4计算机毕业设计-课程设计-期末作业-毕设程序代做
    MySQL查询某字段的多条结果展示成按逗号隔开
    浮点数2^7*29/32中/32是什么意思?
    深入理解作用域、作用域链和闭包
    SSM整合
    SpingMvc入门
    牛客NC357 矩阵第K小【中等 堆 Java、Go、PHP】
  • 原文地址:https://blog.csdn.net/yirenyuan/article/details/127744481