Spring学习(三)——AOP

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.mzph.cn/news/825484.shtml

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

相关文章

2024经常用且免费的10个网盘对比,看看哪个比较好用!

网盘在我们的工作和学习中经常会用到&#xff0c;也是存储资料的必备工具&#xff0c;有了它&#xff0c;我们就不用走到哪都带着移动硬盘了&#xff0c;而目前市场上的主流网盘还有数十款&#xff0c;其中有免费的也有付费的&#xff0c;各家不一&#xff0c;今天小编就来为您…

[Android]模拟器登录Google Play失败

问题&#xff1a; 模拟器登录Google Play失败&#xff0c;提示couldnt sign in there was a problem communicating with google servers. try again later. 原因&#xff1a; 原因是模拟器没有连接到互联网&#xff0c;打开模拟器中Google浏览器进行搜索一样不行。 解决&am…

移动硬盘(PSSD)中文件占用空间远大于文件大小

定义 文件的大小&#xff1a;文件内容实际具有的字节数&#xff0c;它以Byte为衡量单位&#xff0c;只要文件内容和格式不发生变化&#xff0c;文件大小就不会发生变化。 文件占用空间&#xff1a;文件在磁盘上的所占空间&#xff0c;它最小的计量单位是“簇(Cluster)”。 为…

MySQL高负载排查方法最佳实践(15/16)

高负载排查方法 CPU占用率过高问题排查 使用mpstat查看cpu使用情况。 # mpstat 是一款 CPU 性能指标实时展示工具 # 能展示每个 CPU 核的资源视情况&#xff0c;同时还能将资源使用情况进行汇总展示 # 如果CPU0 的 %idle 已经为 0 &#xff0c;说明此核已经非常繁忙# 打印所…

Istio介绍

1.什么是Istio Istio是一个开源的服务网格&#xff08;Service Mesh&#xff09;框架&#xff0c;它提供了一种简单的方式来为部署在Kubernetes等容器编排平台上的微服务应用添加网络功能。Istio的核心功能包括&#xff1a; 服务治理&#xff1a;Istio能够帮助管理服务之间的…

微服务之CircuitBreaker断路器

一、概述 1.1背景 在一个分布式系统中&#xff0c;每个服务都可能会调用其它的服务器&#xff0c;服务之间是相互调用相互依赖。假如微服务A调用微服务B和微服务C&#xff0c;微服务B和微服务C又调用其他的微服务。这就是构成所谓“扇出”。 如果扇出的链路上某个微服务的调…

状态压缩DP题单

P1433 吃奶酪&#xff08;最短路&#xff09; dp(i, s) 表示从 i 出发经过的点的记录为 s 的路线距离最小值 #include<bits/stdc.h> #define int long long using namespace std; const int N 20; signed main() { int n; cin >> n;vector<double>x(n 1),…

C++项目 -- 负载均衡OJ(三)online_judge

C项目 – 负载均衡OJ&#xff08;三&#xff09;online_judge 文章目录 C项目 -- 负载均衡OJ&#xff08;三&#xff09;online_judge一、基于MVC结构的oj服务设计1.结构与功能 二、oj_model.hpp1.建立文件版题库2.文件版题库的服务模块3. MySQL版题库3.1.创建名为oj_client的用…

【uniapp】引入uni-ui组件库

&#xff08;1&#xff09;新建项目的时候选择 uni-ui项目 &#xff08;2&#xff09;已经创建好的项目去官网单独安装 跳转单独安装组件 https://uniapp.dcloud.net.cn/component/uniui/quickstart.html#%E9%80%9A%E8%BF%87-uni-modules-%E5%8D%95%E7%8B%AC%E5%AE%89%E8%A3%8…

202462读书笔记|《一世珍藏的诗歌200首》——你曾经羞赧地向我问起, 是谁最早在此留下足印

202462读书笔记|《一世珍藏的诗歌200首》——你曾经羞赧地向我问起&#xff0c; 是谁最早在此留下足印 《一世珍藏的诗歌200首》作者金宏宇&#xff0c;很多美好的诗&#xff0c;有徐志摩&#xff0c;戴望舒&#xff0c;林徽因&#xff0c;舒婷等的诗精选&#xff0c;很值得一读…

动态库和静态库

文章目录 一、 静态库二、动态库 一、 静态库 静态库&#xff08;.a&#xff09;&#xff1a;程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库&#xff0c;因为他已经在你字节写的程序中。 编译静态库 将所有的.h文件拷贝到lib/include中…

2024年腾讯云服务器价格一览表

随着云计算技术的快速发展&#xff0c;越来越多的企业和个人开始选择使用云服务器来满足他们的数据存储和计算需求。腾讯云作为国内领先的云服务提供商&#xff0c;其服务器产品因性能稳定、安全可靠而备受用户青睐。那么&#xff0c;2024年腾讯云服务器的价格情况如何呢&#…

网络运输层之(3)GRE协议

网络运输层之(3)GRE协议 Author: Once Day Date: 2024年4月8日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文档可参考专栏&#xff1a;通信网络技术_Once-Day的…

OpenHarmony多媒体-video_trimmer

简介 videotrimmer是在OpenHarmony环境下&#xff0c;提供视频剪辑能力的三方库。 效果展示&#xff1a; 安装教程 ohpm install ohos/videotrimmerOpenHarmony ohpm环境配置等更多内容&#xff0c;请参考 如何安装OpenHarmony ohpm包 。 使用说明 目前支持MP4格式。 视频…

ansible模块实战-部署rsync服务端

目录 1、根据部署流程所用到的命令找出模块 2.实战部署 2.1 服务部署&#xff1a;yum 安装 2.2 准备好rsync服务的配置文件 &#xff0c;并将配置文件通过copy模块分发给192.168.81.136这台受控主机 2.3 创建虚拟机用户 2.4 创建密码文件和改权限 2.5 模块对应目录&…

《QT实用小工具·二十九》托盘图标控件

1、概述 源码放在文章末尾 托盘图标控件 可设置托盘图标对应所属主窗体。 可设置托盘图标。 可设置提示信息。 自带右键菜单。 下面是demo演示&#xff1a; 项目部分代码如下&#xff1a; #ifndef TRAYICON_H #define TRAYICON_H/*** 托盘图标控件* 1. 可设置托盘图标…

基于SpringBoot+Vue的大学生心理咨询系统(源码+文档+包运行)

一.系统概述 使用旧方法对学生心理咨询评估信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在学生心理咨询评估信息的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及时纠正等问题。 这次…

Unity解决:导出安卓apk 安装时报错:应用未安装:软件包似乎无效

Unity2018.4.36 导出安卓apk 安装时报错&#xff1a;应用未安装&#xff1a;软件包似乎无效 解决办法&#xff1a;因为安装到安卓12 需要添加添加过滤规则 在AS工程AndroidManifest.xml 添加过滤规则即可。 android:exported"true"

算法训练营第25天回溯(分割)

回溯算法&#xff08;分割&#xff09; 131.分割回文串 力扣题目链接(opens new window) 题目 给定一个字符串 s&#xff0c;将 s 分割成一些子串&#xff0c;使每个子串都是回文串。 返回 s 所有可能的分割方案。 示例: 输入: “aab” 输出: [ [“aa”,“b”], [“a”,“…

Matlab 将数据写入excel文件

Matlab 将数据写入excel文件 函数&#xff1a;writematrix 功能&#xff1a;将数据写入文件 语法 writematrix(A) writematrix(A,filename) writematrix(___,Name,Value) 说明 writematrix(A) 将同构数组 A 写入以逗号分隔的文本文件。文件名为数组的工作区变量名称&…