Spring学习(三)——AOP

news2024/12/23 15:26:50

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

AOP(Aspect Oriented Programming)面向切面编程,在不惊动原始设计的基础上为其进行功能增强,前面咱们有技术就可以实现这样的功能即代理模式。Java设计模式——代理模式-CSDN博客

基础概念

  1. 连接点(Join Point): 连接点是在应用程序执行期间可以插入切面的点。在Spring中,连接点通常是方法调用,尽管它也可以是某些特定的程序执行点,比如异常处理或字段访问。

  2. 切入点(Pointcut): 切入点是一组连接点的集合,其中每个连接点都符合特定的条件。切入点定义了在应用程序中哪些连接点应该被拦截,并应用相应的通知。

  3. 通知(Advice): 通知是在切面的特定连接点上执行的代码。在Spring AOP中,有五种类型的通知:

    • 前置通知(Before Advice):在连接点之前执行。
    • 后置通知(After Advice):在连接点之后执行。
    • 返回通知(After Returning Advice):在连接点正常完成后执行。
    • 异常通知(After Throwing Advice):在连接点抛出异常后执行。
    • 环绕通知(Around Advice):在连接点前后都执行。
  4. 通知类(Advisor): 通知类是将通知和切入点组合在一起的对象。在Spring中,通知类通常是由一个切面定义的,它包含一个或多个通知和一个切入点。

  5. 切面(Aspect): 切面是通知和切入点的组合。它描述了在何处以及何时应用通知。在Spring中,切面通常是一个带有通知方法的普通类,并且使用注解或XML配置来定义切入点和通知。

  AOP实现步骤

步骤1:添加依赖:

aspectjweaver、springframework

步骤2:定义接口与实现类

步骤3:定义通知类和通知

public class MyAdvice {
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

步骤4:定义切入点

public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

步骤5:制作切面

public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    
    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

切面是用来描述通知和切入点之间的关系

步骤6:将通知类配给容器并标识其为切面类

@Aspect

public class MyAdvice {

}

步骤7:开启注解格式AOP功能

@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy
public class SpringConfig {
}

 AOP工作流程

由于AOP是基于Spring容器管理的bean做的增强,所以整个工作过程需要从Spring加载bean说起:

流程1:Spring容器启动

  • * 容器启动就需要去加载bean,哪些类需要被加载呢?
  • * 需要被增强的类,如:BookServiceImpl
  • * 通知类,如:MyAdvice
  • * 注意此时bean对象还没有创建成功

 流程2:读取所有切面配置中的切入点

流程3:初始化bean

判定bean对应的类中的方法是否匹配到任意切入点

  • * 注意第1步在容器启动的时候,bean对象还没有被创建成功。
  • * 要被实例化bean对象的类中的方法和切入点进行匹配

匹配失败,创建原始对象,如`UserDao`

  •      匹配失败说明不需要增强,直接调用原始对象的方法即可。

匹配成功,创建原始对象的代理对象,如:`BookDao`

  •   * 匹配成功说明需要对其进行增强
  •   * 对哪个类做增强,这个类对应的对象就叫做目标对象
  •   * 因为要对目标对象进行功能增强,而采用的技术是动态代理,会为其创建一个代理对象
  •   * 最终运行的是代理对象的方法,在该方法中会对原始方法进行功能增强
流程4:获取bean执行方法
  •  * 获取的bean是原始对象时,调用方法并执行,完成操作
  • * 获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作

 验证容器中是否为代理对象

为了验证IOC容器中创建的对象和我们刚才所说的结论是否一致,首先先把结论理出来:

  • * 如果目标对象中的方法会被增强,那么容器中将存入的是目标对象的代理对象
  • * 如果目标对象中的方法不被增强,那么容器中将存入的是目标对象本身。
  • 当前类的getClass()方法

 AOP配置管理

切入点表达式:

要进行增强的方法的描述方式
execution(public User com.itheima.service.UserService.findById(int))

  • * execution:动作关键字,描述切入点的行为动作,execution表示执行到指定切入点
  • * public:访问修饰符,还可以是public,private等,可以省略
  • * User:返回值,写返回值类型
  • * com.itheima.service:包名,多级包使用点连接
  • * UserService:类/接口名称
  • * findById:方法名
  • * int:参数,直接写参数的类型,多个类型用逗号隔开
  • * 异常名:方法定义中抛出指定异常,可以省略
 通配符

 `*`:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
  execution(public * com.itheima.*.UserService.find*(*))

  匹配com.itheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法

 `..`多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
  execution(public User com..UserService.findById(..))

  匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法

 `+`专用于匹配子类类型
  execution(* *..*Service+.*(..))

  这个使用率较低,描述子类的,咱们做JavaEE开发,继承机会就一次,使用都很慎重,所以很少用它。*Service+,表示所有以Service结尾的接口的子类。

AOP通知类型 

  • 前置通知

  • 后置通知

  • ==环绕通知(重点)==

  • 返回后通知(了解)

  • 抛出异常后通知(了解)

 环绕通知

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    
    @Around("pt()")
    public void around(){
        System.out.println("around before advice ...");
        System.out.println("around after advice ...");
    }
}

ProceedingJoinPoint pjp
 获取原方法、设置返回值类型

@Around("pt2()")
    public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around before advice ...");
        //表示对原始操作的调用
        Object ret = pjp.proceed();
        System.out.println("around after advice ...");
        return ret;
    }

 获取方法名、接口名和签名信息

@Component
@Aspect
public class ProjectAdvice {
    //配置业务层的所有方法
    @Pointcut("execution(* com.itheima.service.*Service.*(..))")
    private void servicePt(){}
    //@Around("ProjectAdvice.servicePt()") 可以简写为下面的方式
    @Around("servicePt()")
    public void runSpeed(ProceedingJoinPoint pjp){
        //获取执行签名信息
        Signature signature = pjp.getSignature();
        //通过签名获取执行操作名称(接口名)
        String className = signature.getDeclaringTypeName();
        //通过签名获取执行操作名称(方法名)
        String methodName = signature.getName();

        
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
           pjp.proceed();
        }
        long end = System.currentTimeMillis();
        System.out.println("万次执行:"+ className+"."+methodName+"---->" +(end-start) + "ms");
    } 
}

通知获取数据

* 获取切入点方法的参数,所有的通知类型都可以获取参数

  •   * JoinPoint:适用于前置、后置、返回后、抛出异常后通知
  •   * ProceedingJoinPoint:适用于环绕通知
    @Before("pt()")
    public void before(JoinPoint jp) 
        Object[] args = jp.getArgs();
        System.out.println(Arrays.toString(args));
        System.out.println("before advice ..." );
    }
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp)throws Throwable {
        Object[] args = pjp.getArgs();
        System.out.println(Arrays.toString(args));
        Object ret = pjp.proceed();
        return ret;
    }

* 获取切入点方法返回值,前置和抛出异常后通知是没有返回值,后置通知可有可无,所以不做研究

  •   * 返回后通知
  •   * 环绕通知
    @AfterReturning(value = "pt()",returning = "ret")
    public void afterReturning(Object ret) {
        System.out.println("afterReturning advice ..."+ret);
    }

    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        Object[] args = pjp.getArgs();
        System.out.println(Arrays.toString(args));
        args[0] = 666;
        Object ret = pjp.proceed(args);
        return ret;
    }

* 获取切入点方法运行异常信息,前置和返回后通知是不会有,后置通知可有可无,所以不做研究

  •   * 抛出异常后通知
  •   * 环绕通知
    @AfterThrowing(value = "pt()",throwing = "t")
    public void afterThrowing(Throwable t) {
        System.out.println("afterThrowing advice ..."+t);
    }
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp){
        Object[] args = pjp.getArgs();
        System.out.println(Arrays.toString(args));
        args[0] = 666;
        Object ret = null;
        try{
            ret = pjp.proceed(args);
        }catch(Throwable throwable){
            t.printStackTrace();
        }
        return ret;
    }

 环绕通知注意事项

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SAM5716B 法国追梦DREAM 音频DSP芯片

法国追梦/DERAM SAM5504/5704/5716/5808音频DSP芯片,开发板&#xff0c;方案 可用于电子鼓、电子琴、电吉他、效果器、均衡器、啸叫抑制器等电声产品领域 全系列芯片&#xff1a; SAM2634 SAM2695 SAM5504B SAM5704B SAM5708B SAM5808B SAM5716B SAM5916B... 原厂开发…

【Qt】Qt Hello World 程序

文章目录 1、Qt Hello World 程序1.1 使用按钮实现1.1.1 使用可视化方式实现 1.1.2 纯代码方式实现 label创建堆&#xff08;内存泄漏&#xff09;或者栈问题Qt基础类&#xff08;Qstring、Qvector、Qlist&#xff09;乱码问题零散知识 1、Qt Hello World 程序 1.1 使用按钮实…

算法学习笔记:Bi-LSTM和Bi-GRU

这篇文章的作为前几篇RNN\LSTM\RNN的后续之作&#xff0c;主要就是补充一个这两个哥的变体&#xff0c;想详细了解RNN\LSTM\GRU的详细理论和公式推导以及代码的请前往下面链接&#xff1a; 算法学习笔记&#xff1a;循环神经网络&#xff08;Recurrent Neural Network)-CSDN博…

udemy视频教程下载:AI和ChatGPT提示工程精通指南

欢迎来到 ChatGPT 大师班&#xff01; 这个 ChatGPT 大师班&#xff1a;AI 和提示工程指南是您通往 AI 未来的全通道通行证。 以下是您的学习旅程&#xff1a; 理解和掌握 ChatGPT&#xff1a;您将深入了解 AI 和语言模型&#xff0c;重点是 ChatGPT。我们设计了这个部分&am…

前端三大件速成 01 HTML

文章目录 一、前端基础知识二、标签1、什么是标签2、标签的属性3、常用标签&#xff08;1&#xff09;声明&#xff08;2&#xff09;注释&#xff08;3&#xff09;html 根标签&#xff08;3&#xff09;head标签&#xff08;4&#xff09;body标签 三、特殊字符四、其他标签1…

java方法递归

简介 案例&#xff1a;阶乘 // 计算一个数的阶乘 public static int factorial(int n) {if (n 1) {return 1;}return n * factorial(n - 1); }案例 猴子吃桃子 // 猴子吃桃子问题 // 第一天吃了一半多一个 第十天剩一个 求第一天有多少个桃子 // 因为 f(x1) f(x)/2 - 1 // 所…

STL库 —— priority_queue 的编写

目录 一、 优先级队列的介绍 二、优先级队列的使用 2.1 建大堆 less 2.2 建小堆 greater 2.3 详解 greater 与 less 三、 priority_queue 的模拟实现 3.1 编写框架 3.2 编写简单函数 3.2 进堆 向上调整 3.3 出堆 向下调整 四、完整代码 一、 优先级队列的介绍 1.…

【Python系列】非异步方法调用异步方法

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

浅谈数据结构---红黑树、二叉树

红黑树简介 红黑树&#xff1a;在本质上还是二叉树&#xff0c;是一种高效的查找树。 特点 一边的数比另一边的数高太多时&#xff0c;自动旋转平衡 当数据量比较大时&#xff0c;层级比较多&#xff0c;查询效率低 如下图所示&#xff1a; 如果一边的数比另一边高太多时&…

AI智能电销机器人是什么?能给我们带来哪些便利?

科技的飞速发展&#xff0c;让很多“懒人”的幻想变成了现实&#xff0c;越来越多的人工智能产品被发明出来甚至完全替代日常生活中的工作。比如在电销行业&#xff0c;很多企业选择AI智能电销机器人进行外呼。那么你了解多少AI智能电销机器人呢&#xff1f;和小编kelaile520一…

前端js控制元素移动

背景 页面中有多个表格&#xff0c;每个表格中均有一从右到左匀速移动的元素&#xff0c;随着元素移动需要在表格中增减数据&#xff0c;由于使用css3动画无法捕捉元素移动位置&#xff0c;所以这里采用js控制dom的写法 解决办法 最终代码放在文章的最后&#xff0c;各位看官…

热塑性聚氨酯TPU的特性有哪些?UV胶水能够粘接热塑性聚氨酯TPU吗?又有哪些优势呢?

热塑性聚氨酯&#xff08;Thermoplastic Polyurethane&#xff0c;TPU&#xff09;是一种具有多种优异性能的弹性塑料&#xff0c;广泛用于各种应用领域。以下是TPU的一些主要特性&#xff1a; 弹性和柔软性&#xff1a; TPU具有良好的弹性和柔软性&#xff0c;能够在受力后迅速…

现在给政府机关医院学校部队供货的方式有哪些?

给政府机关、医院、学校和部队供货的方式主要包括以下几种&#xff1a; 直接采购&#xff1a;政府机关、医院、学校和部队通过招标或直接与供应商进行谈判&#xff0c;确定采购的产品和价格。这种方式常见于大宗或重要物资的采购&#xff0c;能够确保采购过程的透明度和公正性…

林草资源管理系统:构筑绿色长城,守护自然之美

在全球气候变化和生态环境恶化的背景下&#xff0c;森林和草原资源的保护、恢复和合理利用显得尤为重要。林草资源管理系统的建立&#xff0c;旨在通过现代信息技术手段&#xff0c;提升林草资源管理的效率和质量&#xff0c;确保自然资源的可持续发展。 项目背景 森林和草原…

Nacos—配置管理

简介&#xff1a; Nacos是阿里巴巴开发的&#xff0c;它旨在帮助用户更敏捷和容易地构建、交付和管理微服务平台。Nacos的主要功能和特性包括&#xff1a; 动态服务发现。Nacos支持基于DNS和RPC的服务发现&#xff0c;允许服务提供者和消费者之间的高效交互。动态配置管理。…

2024华中杯ABC题完1-3小问py代码+完整思路16页+后续参考论文

A题太阳能路灯光伏板朝向问题 &#xff08;完整版获取在文末&#xff09; 第1小问&#xff1a;计算每月15日的太阳直射强度和总能量 1. 理解太阳直射辐射和光伏板的关系**&#xff1a;光伏板接收太阳辐射并转化为电能&#xff0c;直射辐射对光伏板的效率影响最大。 2. 收集数…

线程互斥,线程安全和线程同步

多线程的基本代码编写步骤 1.创建线程pthread_create() 2.终止线程的三种方法。线程取消pthread_cancel(一般在主线程取消)&#xff0c; 线程终止pthread_exit(在其他线程执行)&#xff0c; 或者使用线程返回return 3.线程等待pthread_join 需要等待的原因是 1.已经退出的线程…

Java程序生成可执行的exe文件 详细图文教程

1.Java编辑器&#xff0c;如&#xff1a;idea、eclipse等&#xff0c;下载地址&#xff1a;IntelliJ IDEA: The Capable & Ergonomic Java IDE by JetBrainshttps://www.jetbrains.com/idea/2.exe4j&#xff0c;下载地址&#xff1a;ej-technologies - Java APM, Java Prof…

spring 集成 mybatis

spring 集成 mybatis 1、spring对junit的支持1.1、对junit4的支持1.2 对junit5的支持 2、Spring6集成MyBatis3.52.1 实现步骤2.2 实现 1、spring对junit的支持 1.1、对junit4的支持 依赖 <?xml version"1.0" encoding"UTF-8"?> <project xml…

基于XML配置bean(一)

文章目录 1.获取bean的两种方式1.通过id获取bean&#xff08;前面用过&#xff09;2.通过类型获取bean&#xff08;单例时使用&#xff09;1.案例2.代码1.beans.xml2.SpringBeanTest.java3.结果 3.注意事项 2.三种基本依赖注入方式1.通过属性配置bean&#xff08;前面用过&…