Spring的另一大的特征:AOP

news2025/4/7 3:36:51

目录

  • AOP (Aspect Oriented Programming)
  • AOP 入门案例(注解版)
  • AOP 工作流程——代理
  • AOP切入点表达式
  • AOP 通知类型
  • AOP通知获取数据
    • 获取切入点方法的参数
    • 获取切入点方法返回值
    • 获取切入点方法运行异常信息
  • 百度网盘分享链接输入密码数据错误兼容性处理

AOP (Aspect Oriented Programming)

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

OOP (Object Oriented Programming) 面向对象编程

  • AOP 的作用: 在不惊动原始设计的基础上为其进行功能增强
  • Spring 理念: 无入侵式/无侵入式

在这里插入图片描述

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

    • 在 SpringAOP 中,理解为方法的执行
  • 切入点(Pointcut): 匹配连接点的式子

    • 在 SpringAOP 中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
      • 一个具体方法:eg : com.itheima.dao包下的BookDao接口中的无形参无返回值的save方法
      • 匹配多个方法:eg : 所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法
  • 通知(Advice): 在切入点处执行的操作,也就是共性功能

    • 在 SpringAOP 中,功能最终以方法的形式呈现
  • 通知类: 定义通知的类

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

AOP 入门案例(注解版)

- 导入 aop 相关坐标
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.4</version>
    </dependency>

spring-context 坐标依赖 spring-aop 坐标
在这里插入图片描述

    • 定义dao 接口与实现类
public interface BookDao {
    public void save();

    public void update();

    public void delete();

    public void select();
}

@Repository
public class BookDaoImpl implements BookDao {

    public void save() {
        //记录程序当前执行执行(开始时间)
        Long startTime = System.currentTimeMillis();
        //业务执行万次
        for (int i = 0;i<10000;i++) {
            System.out.println("book dao save ...");
        }
        //记录程序当前执行时间(结束时间)
        Long endTime = System.currentTimeMillis();
        //计算时间差
        Long totalTime = endTime-startTime;
        //输出信息
        System.out.println("执行万次消耗时间:" + totalTime + "ms");
    }

    public void update(){
        System.out.println("book dao update ...");
    }

    public void delete(){
        System.out.println("book dao delete ...");
    }

    public void select(){
        System.out.println("book dao select ...");
    }
}

    • 定义通知类,制作通知
    • 定义切入点:切入点定义依托一个不具有实际意义的方法进行,即无参数,无返回值,方法体无实际逻辑
    • 绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行位置
    • 定义通知类受Spring容器管理,并定义当前类为切面类
@Component
@Aspect
public class MyAdvice {

    @Pointcut("execution(* com.itheima.dao.BookDao.*d*(..))")
    private void pt(){}

    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("------------------------------");
        Long startTime = System.currentTimeMillis();
        for (int i = 0 ; i<10000 ; i++) {
            //调用原始操作
            pjp.proceed();
        }
        Long endTime = System.currentTimeMillis();
        Long totalTime = endTime-startTime;
        System.out.println("执行万次消耗时间:" + totalTime + "ms");
        return null;
    }

}
    • 开启 Spring 对 AOP 注解驱动支持 : @EnableAspectJAutoProxy
@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy
public class SpringConfig {
}

AOP 工作流程——代理

  1. Spring 容器启动
  2. 读取所有切面配置中的切入点
  3. 初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
    • 匹配失败,创建对象
    • 匹配成功,创建原始对象(目标对象)的代理对象
  4. 获取bean执行方法
    • 获取bean,调用方法并执行,完成操作
    • 获取bean是代理对象时, 根据代理对象的运行模式运行原始方法与增强的内容,完成操作
  • 目标对象(Target) : 原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的。
  • 代理(Proxy) : 目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现

AOP切入点表达式

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

在这里插入图片描述

  • 切入点表达式标准格式: 动作关键字 (访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)
execution (public User com.itheima.service.UserService.findById (int))
    • 动作关键字: 描述切入点的行为动作,例如execution表示执行到指定切入点
    • 访问修饰符: public , private 等 可省略
    • 返回值
    • 包名
    • 类/接口名
    • 方法名
    • 参数
    • 异常名:方法定义中抛出指定异常,可省略
  • 可以使用通配符描述切入点,快速描述:

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

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

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

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

    • eg: + :专用于匹配子类类型
execution (* *..*Service+.*(..))

AOP 通知类型

  • AOP 通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置
  • 5 种类型
    • 前置通知
    • 后置通知
    • 环绕通知 (重点) :通过ProceedingJoinPoint 对原始方法调用,获得返回值,最终返回这个返回值
    • 返回后通知 (了解)
    • 抛出异常后通知 (了解)
@Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}

@Pointcut("execution(int com.itheima.dao.BookDao.select())")
    private void pt2(){}

    //@Before:前置通知,在原始方法运行之前执行
//    @Before("pt()")
    public void before() {
        System.out.println("before advice ...");
    }

    //@After:后置通知,在原始方法运行之后执行
//    @After("pt2()")
    public void after() {
        System.out.println("after advice ...");
    }

    //@Around:环绕通知,在原始方法运行的前后执行
//    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around before advice ...");
        //表示对原始操作的调用
        Object ret = pjp.proceed();
        System.out.println("around after advice ...");
        return ret;
    }

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

    //@AfterReturning:返回后通知,在原始方法执行完毕后运行,且原始方法执行过程中未出现异常现象
//    @AfterReturning("pt2()")
    public void afterReturning() {
        System.out.println("afterReturning advice ...");
    }

    //@AfterThrowing:抛出异常后通知,在原始方法执行过程中出现异常后运行
    @AfterThrowing("pt2()")
    public void afterThrowing() {
        System.out.println("afterThrowing advice ...");
    }
}
  • @Around 注意事项
    • 环绕通知必须依赖形参 ProceedingJoinPoint 才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知
    • 通知中如果未使用 ProceedingJoinPoint 对原始方法进行调用将跳过原始方法的执行
    • 对原始方法的调用可以不接收返回值,通知方法设置成void即可,如果接收返回值,必须设定为Object类型
    • 原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object
    • 由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象

AOP通知获取数据

获取切入点方法的参数

  • JoinPoint: 适用于前置、后置、返回后、抛出异常后通知
  • JoinPoint 对象描述了连接点方法的运行状态,可以获取到原始方法的调用参数
	@Before("pt()")
    public void before(JoinPoint jp) {
        Object[] args = jp.getArgs();
        System.out.println(Arrays.toString(args));
        System.out.println("before advice ..." );
    }
  • ProceedJoinPoint: 适用于环绕通知, 是 JoinPoint 的子类
	@Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        Object[] args = pjp.getArgs();
        System.out.println(Arrays.toString(args));
        ret = pjp.proceed(args);
        return ret;
    }

获取切入点方法返回值

  • 返回后通知
    //设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同
    @AfterReturning(value = "pt()",returning = "ret")
    public void afterReturning(JoinPoint jp, String ret) {
        System.out.println("afterReturning advice ..."+ret);
    }
  • 环绕通知 可以手工书写对原始方法的调用,得到的结果即为原始方法的返回值。
	@Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        ret = pjp.proceed(args);
        return ret;
    }

获取切入点方法运行异常信息

  • 抛出异常后通知:可以获取切入点方法中出现的异常信息,使用形参可以接收对应的异常对象
    //设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同
    @AfterThrowing(value = "pt()",throwing = "t")
    public void afterThrowing(Throwable t) {
        System.out.println("afterThrowing advice ..."+t);
    }
  • 环绕通知
	@Around("pt()")
    public Object around(ProceedingJoinPoint pjp) {
        Object ret = null;
        try {
            ret = pjp.proceed(args);
        } catch (Throwable t) {
            t.printStackTrace();
        }
        return ret;
    }

百度网盘分享链接输入密码数据错误兼容性处理

@Around("DataAdvice.servicePt()")
public Object trimString(ProceedingJoinPoint pjp) throws Throwable {
	Object[] args = pjp.getArgs();
	//对原始参数中的每一个参数进行操作
	for (int i = 0; i < args.length; i++) {
		if (args[i].getClass().equals(String.class)) {
			args[i] = args[i].toString().trim();
		}
	}
	return pjp.proceed(args);
}

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

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

相关文章

[数据集][目标检测]游泳者溺水数据集VOC+YOLO格式2类别895张

数据集制作单位&#xff1a;未来自主研究中心(FIRC) 数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;895 标注数量(xml文件个数)&#xff1a…

第7.1章:StarRocks性能调优——查询分析

目录 一、查看查询计划 1.1 概述 1.2 查询计划树 1.3 查看查询计划的命令 1.3 查看查询计划 二、查看查询Profile 2.1 启用 Query Profile 2.2 获取 Query Profile 2.3 Query Profile结构与详细指标 2.3.1 Query Profile的结构 2.3.2 Query Profile的合并策略 2.…

计算机视觉基础知识(十五)--卷积神经网络

卷积神经网络简介 CNN--卷积神经网络&#xff0c;是一种前馈神经网络&#xff1b;不同于传统的只有线性连接的神经网络&#xff1b;CNN具有卷积&#xff08;convolution&#xff09;操作、池化&#xff08;pooling&#xff09;和非线性激活函数映射等&#xff1b;经典CNN网络有…

【Linux】--- 详解Linux软件包管理器yum和编辑器vim

目录 一、Linux软件包管理器 - yum1.1 yum和软件包是什么1.2 Linux系统(Centos)的生态1.3 yum相关操作1.4 yum本地配置 二、Linux编辑器 - vim使用2.1 vim的基本概念2.2 vim命令模式命令集2.3 vim末行模式命令集2.4 关于vim的几个相关问题 一、Linux软件包管理器 - yum 1.1 yu…

Codeforce Monsters Attack!(B题 前缀和)

题目描述&#xff1a; 思路&#xff1a; 本人第一次的想法是先杀血量低的第二次想法是先搞坐标近的第三次想法看到数据量这么大&#xff0c; 我先加个和看看貌似我先打谁都行&#xff0c;由此综合一下&#xff0c; 我们可以把每一个不同的坐标当作一轮从最小的坐标开始&#x…

Vue+SpringBoot打造在线课程教学系统

目录 一、摘要1.1 系统介绍1.2 项目录屏 二、研究内容2.1 课程类型管理模块2.2 课程管理模块2.3 课时管理模块2.4 课程交互模块2.5 系统基础模块 三、系统设计3.1 用例设计3.2 数据库设计 四、系统展示4.1 管理后台4.2 用户网页 五、样例代码5.1 新增课程类型5.2 网站登录5.3 课…

机器学习打分函数在分子对接中的应用系列-GB_Score

欢迎浏览我的CSND博客&#xff01; Blockbuater_drug …点击进入 文章目录 前言一、GB-Score是什么&#xff1f;二、文献复现 -训练和验证环境1. GB score验证虚拟环境的配置2. Usage1- Preparing ligand and protein file2- Generating features3 - Repeat and extend current…

打印水仙花数---c语言刷题

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 题述 求出0&#xff5e;100000之间的所有“水仙花数”并输出。 “水仙花数”是指一个n位数&#xff0c;其各位数字的n次方之和确好等于该数本身&#xff0c;如:153&#…

openssl3.2 - crypto-mdebug被弃用后, 内存泄漏检查的替代方法

文章目录 openssl3.2 - crypto-mdebug被弃用后, 内存泄漏检查的替代方法概述笔记查看特性列表openssl3.2编译脚本 - 加入enable-crypto-mdebug看看有没有替代内存诊断的方法?main.cppmy_openSSL_lib.hmy_openSSL_lib.c备注备注END openssl3.2 - crypto-mdebug被弃用后, 内存泄…

sonar-java 手写一个规则-单元测试分析

前言 最近做项目&#xff0c;定制sonar规则&#xff0c;提高Java代码质量&#xff0c;在编写的sonar规则&#xff0c;做验证时&#xff0c;使用单元测试有一些简单的心得感悟&#xff0c;分享出来。 自定义规则模式 sonar的自定义规则很简单&#xff0c;一般而言有2种模式可…

解决vulhub漏洞环境下载慢卡死问题即解决docker-valhub漏洞环境下载慢的问题

解决vulhub环境下载慢/卡 当前环境为&#xff1a;ubuntu20 1.在 cd /etc/docker/目录下创建或修改daemon.json文件 sudo touch daemon.json编辑daemon.json文件 sudo vim daemon.json2.填写阿里云镜像地址&#xff1a; { "registry-mirrors":["https://6kx…

UE5 C++ Gas开发 学习记录(三)

添加AuraPlayerState,AuraAbilitySystemComponentBase和AuraAttributeSet 在Build.cs里添加 // Copyright Epic Games, Inc. All Rights Reserved. using UnrealBuildTool; public class MyGas : ModuleRules { public MyGas(ReadOnlyTargetRules Target) : base(Target) { P…

Android pixel6网页线刷官方镜像

背景 p6-7系pixel更新Android 13,首次引入了防回滚机制,Android13系统检测到属于Android12版本的旧bootloader就会直接红字警告拒绝引导,多次引导就会成为收藏品!(除非充钱) 今天收到了新的pixel6,想着以后刷机刷内核肯定频繁,就先打个预防针。 网页线刷官方镜像 查…

RabbitMQ-消息队列:优先级队列、惰性队列

20、优先级队列 在我们系统中有一个订单催付的场景&#xff0c;我们的客户在天猫下的订单&#xff0c;淘宝会及时将订单推送给我们&#xff0c;如果在用户设定的时间内未付款那么就会给用户推送一条短信提醒&#xff0c;很简单的一个功能对吧。 但是&#xff0c;天猫商家对我…

如何增加层次厚度?

Q 老师&#xff0c;我在做一个斧头武器&#xff0c;如何在平面上增加厚度和层次呢&#xff1f; A 选中这几个线&#xff0c;点连接就会出现中线&#xff0c;把中线稍作调整即可~

[嵌入式系统-35]:RT-Thread -20- 新手指南:在Keil MDK-ARM 模拟器上运行RT-Thread

目录 前言&#xff1a; 一、Keil MDK-ARM 模拟器概述 1.1 Keil概述 1.2 Keil MDK-ARM 1.3 Keil MDK-ARM软件仿真模拟器 1.4 Keil模拟器支持的CPU类型 二、Keil MDK ARM安装 前言&#xff1a; 一般嵌入式操作系统因为它的特殊性&#xff0c;往往和硬件平台密切相关连&am…

【C++精简版回顾】12.友元函数

1.友元函数 1.class class MM { public:MM(int age,string name):age(age),name(name){}friend void print(MM mm); private:int age;string name;void print() {cout << age << "岁的" << name << "喜欢你" << endl;} }; f…

DAY31--learning English

一、积累 1.stiff 2.curve 3.pungent 4.mellow 5.marinate 6.drain 7.corrupt 8.magnet 9.liver 10.stew 11.maid 12.sonogram 13.folk 14.anecdote 15.altar 16.spaghetti 17.ammunition 18.shield 19.beacon 20.hanger 二、练习 1.牛津原译 stiff /stɪf/ ( stiffer stiff…

编程的基础:理解时间和空间复杂度

编程的基础&#xff1a;理解时间和空间复杂度 时间复杂度空间复杂度示例常数时间复杂度 O(1)线性时间复杂度 O(n)线性对数时间复杂度 O(n log n)二次时间复杂度 O(n^2)指数时间复杂度 O(2^n) 空间复杂度示例常数空间复杂度 O(1)线性空间复杂度 O(n)线性对数空间复杂度 O(log n)…

流浪动物救助平台:Java开发的实践与思考

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…