猿创征文|Spring系列框架之面向切面编程AOP

news2024/12/23 19:41:30

⭐️前面的话⭐️

本篇文章将介绍一种特别重要的思想,AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。

AOP把软件系统分为两个部分:核心关注点横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。

📒博客主页:未见花闻的博客主页
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
📌本文由未见花闻原创,CSDN首发!
📆首发时间:🌴2022年9月6日🌴
📆修改时间:🌴2023年4月11日🌴
✉️坚持和努力一定能换来诗与远方!
💭推荐书籍:📚《Spring实战》,📚《SpringBoot实战》
💬参考在线编程网站:🌐牛客网🌐力扣
博主的码云gitee,平常博主写的程序代码都在里面。
博主的github,平常博主写的程序代码都在里面。
🍭作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!


📌导航小助手📌

  • 1.面向切面编程AOP
    • 1.1什么是AOP?
    • 1.2AOP的作用
    • 1.3AOP的核心概念
  • 2.Spring AOP
    • 2.1Spring AOP的使用
    • 2.2AspectJ表达式基本语法
    • 2.3抛出异常后通知与环绕通知
    • 2.4Spring AOP的实现原理


封面区


1.面向切面编程AOP

1.1什么是AOP?

AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

使用"横切"技术,AOP把软件系统分为两个部分:核心关注点横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

1.2AOP的作用

想象一个场景,我们在做后台系统时,除了登录和注册等几个功能不需要做用户登录验证之外,其他几乎所有页面都需要先验证用户登录的状态,那这个时候我们要怎么处理呢?

如果不使用AOP,我们就需要在每一个Controller层都写一遍验证用户是否已经登录的程序,如果你实现的功能有很多,并且这些功能都需要进行登录验证,那你就需要编写大量重复的代码,非常的麻烦,尽管你可以将登录验证实现的逻辑封装在一个方法中,但是你要在很多地方调用这个方法,还是很麻烦。

如果使用AOP,在进入核心的业务代码之前会做统一的一个拦截,去验证用户是否登录,这样就很方便,仅需做一个拦截工作,再将验证代码一执行即可。

除了登录验证功能之外,还有很多功能也可以使用AOP,比如:

  • 统一日志记录与持久化。
  • 统一方法执行时间统计。
  • 统一数据返回格式。
  • 统一处理程序中的异常。
  • 统一事务的开启与提交。

也就是说使用 AOP 可以扩充多个对象的某个能力,所以 AOP 可以说是 OOP (Object Oriented Programming,面向对象编程)的补充和完善。

1.3AOP的核心概念

1、横切关注点

想要对哪些方法或类进行拦截,拦截后怎么处理,这些关注点称之为横切关注点。

2、切面(aspect)

类是对物体特征的抽象,切面就是对横切关注点的抽象,你可以认为切面相当于横切关注点。

3、连接点(joinpoint)

被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器。

4、切入点(pointcut)

提供一组规则,根据规则匹配合法的连接点,满足规则的连接点可以理解为切点,然后可以为切点提供具体的处理(通知)。

5、通知(advice)

所谓通知指的就是指拦截到连接点之后要执行的代码,或者说在切点出所需要执行的代码是什么。

通知包含前置通知,后置通知,返回之后通知,抛异常后通知与环绕通知五类。

在Spring切面类中,可以在方法上使用以下注解,会设置方法为通知方法,在满足条件后会调用对应满足条件的方法:

  • 前置通知使用@Before∶通知方法会在目标方法调用之前执行。
  • 后置通知使用@After∶通知方法会在目标方法返回或者抛出异常后调用。
  • 返回之后通知使用@AfterReturning∶ 通知方法会在目标方法返回后调用。
  • 抛异常后通知使用@AfterThrowing∶ 通知方法会在目标方法抛出异常后调用。
  • 环绕通知使用@Around∶通知包裹了被通知的方法,在被通知的方法通知之前和调用之后执行自定义的行为。

1

6、目标对象

代理的目标对象。

7、织入(weaving)

织入(weaving)即代理的生成时机,
织入是把切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中。
在目标对象的生命周期里有多个点可以进行织入∶

  • 编译期∶切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的。
  • 类加载器∶切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入(load-time weaving.LTW)就支持以这种方式织入切面。
  • 运行期∶切面在应用运行的某一时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态创建一个代理对象。SpringAOP就是以这种方式织入切面的。

8、引入(introduction)

在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段。

2.Spring AOP

面向切面编程是一种思想,Spring AOP是AOP的一种实现。

2.1Spring AOP的使用

SpringAOP使用的主要步骤为:
第一步,在SpringBoot项目中添加AOP相关的依赖。
第二步,定义切面。
第三步,定义切点。
第四步,实现通知。

第一步,在SpringBoot项目中添加AOP相关的依赖,就是在Maven的配置文件中添加aop的依赖。
由于使用Edit Starters插件访问官方的源是找不到有关SpringBoot的AOP依赖,这是因为在idea中,上面只列举了一些常用的依赖,不是所有依赖都在上面,如果找不到我们就去Maven中央仓库中去寻找。

搜索一下,找到这个依赖,然后进去复制依赖信息拷贝到Maven的配置文件中就行。
2

		<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>

第二步,定义切面,在spring boot项目中其实就是加上@Aspect和@Component注解的一个类,这个类就表示一个切面。

//设置切面,这个类就是一个切面
@Aspect
@Component
public class UserAspect {
	...
}

第三步,在切面里面定义切点,在Spring中其实本质上就是一个方法,具体说是使用 @Pointcut注解修饰的一个方法,该方法不需要配置任何信息。

    //定义切点,设置拦截规则
    @Pointcut("execution(* com.example.demo.controller.UserController.* (..))")
    public void pointcut() {

    }

其中@Pointcut注解中的参数是一个AspectJ表达式,它的作用就是设置哪些返回值类型哪些类的哪些方法需要拦截可以指定到参数列表。
4

第四步,实现通知,本质上就是实现一个方法,只不过在方法上加上不同通知类型的注解即可,如前置通知加上@Before注解,注解的参数为切点方法名。

    //前置通知
    @Before("pointcut()")
    public void doBefore() {
        System.out.println("执行Before通知");
    }

同理,后置通知也是如此,就是将注解改为@After:

    //后置通知
    @After("pointcut()")
    public void doAfter() {
        System.out.println("执行After通知");
    }

以及目标方法返回后通知@AfterReturning:

    //返回之后通知
    @AfterReturning("pointcut()")
    public void doAfterRunning() {
        System.out.println("执行AfterRunning通知");
    }

我们来验证一下上述设置切面拦截代码的正确性,我们写一个在拦截范围的类以及方法:
5
启动程序,我们访问页面http://127.0.0.1:8080/user/hello,看看控制台的输出:
6
通过运行结果我们也能够看出上面三种通知方式执行的时机以及先后顺序。

2.2AspectJ表达式基本语法

*∶匹配任意字符,只匹配一个元素(包,类,或方法,方法参数)
..∶匹配任意字符,可以匹配多个元素,在表示类时,必须和*联合使用,匹配参数列表时表示匹配所有类型的参数列表。
+ ∶ 表示按照类型匹配指定类及其所有子类,必须跟在类名后面,如com.Car+,表示拦截Cat类以及继承Cat类的所有子类。

切点表达式由切点函数组成,其中execution()是最常用的切点函数,用来匹配方法,语法为∶

execution(<权限修饰符><返回类型><..方法(参数)><异常>)

其中权限修饰符与异常项一般省略,返回类型方法以及参数不可省略,其他项可以省略。

权限修饰符:

  • 填写权限修饰符,就只会匹配相应修饰符修饰的方法。
  • 省略,权限不作为限制,所有权限修饰符的方法都会匹配。

返回类型,必须参数,不可省略:

  • 填写具体返回类型,就匹配相应返回类型的方法。
  • *表示匹配所有返回值类型的方法。

包,类,一般情况下要有,但是可以省略:

  • 填写包和类,就只匹配你所规定的包或类。
  • *表示匹配某目录下所有的包或者类。
  • +作用在类上,匹配该类以及继承该类的所有子类。

方法,表示需要匹配方法的名字,参数表示需要匹配参数列表的类型,不可省略:

  • 指定方法名和参数列表,就只匹配你所限定的方法。
  • *可以作用在方法匹配字段上,表示匹配某类中所有的方法。
  • ..可以作用在参数列表上,对参数列表类型不做限制。

异常,可以匹配抛出指定异常的方法,该参数一般省略。

下面来看几个例子,我们来了解一下AspectJ表达式:

execution(* com.cad.demo.User.*(..))∶匹配User类里的所有方法。
execution(* com.cad.demo.User+.*(..))∶匹配User类及其子类中的所有方法。
execution(* com.cad.*.*(..))∶匹配com.cad包下的所有类的所有方法。
execution(* com.cad..*.*(..))∶匹配 com.cad 包下、子孙包下所有类的所有方法。
execution(* addUser(String,int))∶ 匹配 addUser 方法,且第一个参数类型是 String,第二个参数类型是int。

2.3抛出异常后通知与环绕通知

前面我们已经介绍了前置通知,后者通知以及返回后通知的演示,下面我们继续介绍剩下两种通知,在上面已经实现代码基础上,我们继续添加通知来进行演示。

抛出异常后通知,其实和前面三种通知的用法可以说一模一样,只不过只有当程序出现异常的时候才会执行该通知,写法如下,就是在切面类中实现一个方法,使用@AfterThrowing注解修饰即可:

    //抛异常后通知
    @AfterThrowing("pointcut()")
    public void doAfterThrowing() {
        System.out.println("抛出异常后,执行AfterThrowing通知");
    }

然后我们再在目标方法中构造一个异常,异常随便写一个异常就行,如算术异常:
8

我们访问页面http://127.0.0.1:8080/user/world来看一看控制台输出:
9
由于出现了异常,方法被强制终止了,没有返回,所以没有返回后通知。

最后还剩下一个环绕通知,环绕通知你可以理解为将前置通知和后置通知一体化了,环绕通知最常见的用法之一就是计算目标方法执行的时间是多少,使用其他通知无法做到,如果使用前置加后置通知进行对目标方法的计时,在单线程下没有问题,但是在多线程下有问题,当一个线程正在计时时,另外一个线程调用了前置通知,此时计时开始的时间就被刷新了,那自然计算得到的目标方法执行时间也就不准确了,而环绕通知使一体化的,不存在类似这种线程安全的问题。

环绕通知相比于其他的三种通知的使用方法较为复杂,首先实现环绕通知的方法必须含有ProceedingJoinPoint类的参数和使用 @Around注解修饰,表示连接点的执行进度,方法体里面第一步是执行环绕方法的前置通知,然后通过该类对象获取目标方法执行进度并调用,再执行环绕通知的后置通知,最后并返回该目标方法进度。

    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) {
        Object res = null;
        System.out.println("执行环绕通知前置通知");

        try {
            //根据连接点进度获取目标方法,并执行目标方法
            res = joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("环绕通知后置通知");
        return res;
    }

我们访问页面http://127.0.0.1:8080/user/hello来看一看控制台输出:
10

我们可以基于环绕通知实现对目标方法的计时功能:
实现思路很简单,就是在执行目标方法之前开始计时,执行完目标方法之后结束计时,差值就是方法运行的时间。

计时的方式可以使用时间戳或者spring中的StopWatch类,后者更准确一点,其实都差不多。

我们可以通过传入的joinPoint对象获取目标方法的方法名以及具体所在类和包,joinPoint.getSignature().toString()就能生成目标方法的全部有关名字的信息,我们可以加上一个方法的信息来表示哪一个方法执行的时间。

    @Around("pointcut()")
    public Object doTime(ProceedingJoinPoint joinPoint) {
        Object result = null;
        //System.out.println("环绕通知前置通知");
        String methodName = "";
        long start = 0;
        long end = 0;
        StopWatch stopWatch = new StopWatch();
        try {
            //执行拦截方法
            start = System.currentTimeMillis();
            stopWatch.start();

            methodName = joinPoint.getSignature().toString();
            result = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        } finally {
            end = System.currentTimeMillis();
            stopWatch.stop();
        }
        //System.out.println("环绕通知后置通知");
        System.out.println(methodName + "执行了" + (end - start) + "ms");
        System.out.println(methodName + "执行了" + (stopWatch.getTotalTimeMillis()) + "ms");
        return result;
    }

运行结果:
11

2.4Spring AOP的实现原理

Spring AOP是构建在动态代理基础上,因此 Spring对AOP的支持局限于方法级别的拦截。

Spring AOP支持JDKProxy 和CGLIBProxy方式实现动态代理。默认情况下,对于非final修饰的类,SpringAOP会基于CGLIBProxy生成代理类,CGLIBProxy生成代理类的原理就是继承目标类,被关键字final修饰的类,由于不能被继承,所以会基于DKProxy生成代理类。
13
SpringAOP的本质就是生成一个目标对象的代理类,当前端传来请求时,不会将请求直接交给目标对象,而是首先代理类进行处理,如果满足一定的条件,才会将请求交给目标对象。

如果处理请求前需要登录验证,那么代理类会去验证用户账户是否登录,如果用户登录了才会将请求交给目标对象并执行核心业务代码,否则代理类之间返回响应让用户先登录。


参考 & 资料

Spring3:AOP

觉得文章写得不错的老铁们,点赞评论关注走一波!谢谢啦!

1-99

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

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

相关文章

Springcloud1---->Zuul网关

目录 简介加入zuul后的架构快速入门添加Zuul依赖编写zuul启动类编写zuul配置文件编写路由规则 面向服务的路由添加Eureka客户端依赖开启Eureka客户端发现功能添加Eureka配置&#xff0c;获取服务信息修改映射配置&#xff0c;通过服务名称获取 简化的路由配置过滤器使用场景自定…

这个 堆排序详解过程 我能吹一辈子!!!

文章目录 堆排序的概念堆的分类堆排序的算法思想堆排序的实现 堆排序的概念 堆是一种叫做完全二叉树的数据结构&#xff0c;可分为大根堆、小根堆&#xff0c;而堆排序就是基于这种结构产生的一种排序的算法。 堆的分类 大根堆&#xff1a;每个节点的值都大于或者等于它的左…

SpringBoot 读取 yml 文件属性值常用法总结

开发过程中有一些常量配置一般会写在application.yml文件中&#xff0c;而Spring Boot读取yml文件的主要方式有以下几种: 一、使用Value注解 在bean的属性上使用Value注解,直接读取yml中的值,如: 但这里面写法也有一些情况&#xff1a;其实这种写法对于 String 字符串其实没有…

计算机网络考试周极限复习--1

第一章 时延 因特网协议栈和OSI参考模型 应用层&#xff1a;报文 HTTP&#xff08;提供了Web文档的请求和传送&#xff09;&#xff0c;SMP&#xff08;提供了电子邮件报文的传送&#xff09;&#xff0c; FTP&#xff08;它提供两个端系统之间的文件传送&#xff09; 运输…

【线下|05.27】|StarRocks Friends 杭州站

StarRocks & Friends 是由 StarRocks 社区发起的城市线下 meetup&#xff0c;旨在联合社区与行业的专家小伙伴们分享基于 StarRocks 的最佳实践、大数据分析的前沿技术和 StarRocks 生态融合等热门话题。 不远千里奔赴&#xff0c;只为与你相聚。这个夏天&#xff0c;让我们…

Python大火,零基础还能学习么?

Python近段时间一直涨势迅猛&#xff0c;在各大编程排行榜中崭露头角&#xff0c;得益于它多功能性和简单易上手的特性&#xff0c;让它可以在很多不同的工作中发挥重大作用。 正因如此&#xff0c;目前几乎所有大中型互联网企业都在使用 Python 完成各种各样的工作&#xff0…

广义状态平均无线电能传输系统建模

关于WPT系统建模的一些笔记&#xff0c;在 CSDN 学到很多&#xff0c;现分享给大家&#xff0c;之前有看到过一篇博文&#xff0c; 内容语焉不详&#xff0c;对读者也很不客气&#xff0c;希望这篇博文对大家有用&#xff01; Hierarchical multiobjective H-infinity robust …

Midjourney8种风格介绍+使用场景(3)

引言 我相信大家都或多或少玩过Midjourney&#xff0c;但是要形成自己独特的个人IP&#xff0c;那么有必要知晓画作的一些基础知识&#xff0c;如果你没有时间实践&#xff0c;没有关系&#xff0c;我来操作&#xff0c;定期分享画作相关知识&#xff0c;既简单又方便&#xff…

Systrace系列4 —— SystemServer 解读

本文主要是对 SystemServer 进行简单介绍,介绍了 SystemServer 中几个比较重要的线程,由于 Input 和 Binder 比较重要,所以单独拿出来讲,在这里就没有再涉及到。 窗口动画 Systrace 中的 SystemServer 一个比较重要的地方就是窗口动画,由于窗口归 SystemServer 来管,那么…

CentOS离线配置Java环境

CentOS离线配置Java环境 环境&#xff1a; 操作系统&#xff1a;Linux-CentOS 7Java版本&#xff1a;JDK17远程连接工具&#xff1a;MobaXterm 1.JDK下载 官网下载&#xff1a;https://www.oracle.com/cn/java/technologies/downloads/#java17 ​ 因为MobaXterm自带Sftp&am…

【python csv、Excel、json】零基础也能轻松掌握的学习路线与参考资料

CSV、Excel、JSON 是常用的数据存储格式&#xff0c;分别在不同的场景下有其特点和应用。下面将从以下几个方面进行比较&#xff1a;格式、特点、应用场景和优秀实践。 1.格式 CSV&#xff08;Comma-Separated Values&#xff0c;逗号分隔值&#xff09;格式是一种以纯文本形…

Contrastive Triplet Center Loss

Contrastive Loss background&#xff1a; 最直接的想法是我们假设存在一个损失函数&#xff0c;它满足如下的基本准则 近似样本之间的距离越小越好不似样本之间的距离越大越好 相似样本的坐标被放的越来越远&#xff0c;不似样本之间的距离越来越大&#xff0c;但训练的目标…

V神透露以太坊发展规划 未来十年,zkS将与区块链一样重要

作为加密世界&#xff0c;除中本聪外颇为“传奇”的人物&#xff0c;以太坊联合创始人V神眼光向来毒辣&#xff0c;在加密领域、区块链产业取得诸多“卓著”成绩。 在近期举行的EDCON 2023盛会上&#xff0c;V神透露了以太坊2.0的最新进展和未来规划&#xff0c;以及他对以太坊…

MySQL之索引初步

1. 索引概念 数据库是⽤来存储数据&#xff0c;在互联⽹应⽤中数据库中存储的数据可能会很多(⼤数据)&#xff0c; 数据表中数据的查询速度会随着数据量的增⻓而逐渐变慢 &#xff0c;从⽽导致响应⽤户请求的速度变慢——⽤户体验差&#xff0c;我们如何提⾼数据库的查询效率呢…

射频电容物位开关电容传感器的分类与应用

​射频电容物位开关电容传感器是一种常见的物位检测装置&#xff0c;广泛应用于粉状物料、液体、颗粒物料等不同类型的物料的检测和控制。本文将对射频电容物位开关电容传感器的分类和应用进行详细介绍。 一、射频电容物位开关电容传感器的工作原理 射频电容物位开关电容传感…

perl 通过 swig 调用 c++代码

Swig 是一个软件开发工具&#xff0c;可以简化不同语言与 C/C 的交互&#xff08;直接在其它语言的代码中调用 C/C 的代码&#xff09;。   记录一下成功用 perl 调用 c 代码的例子。 环境 操作系统&#xff1a;centos 7.9 perl: version 5.16.3 swig: version 2.0.10 g: v…

C++中map的用法

博主简介&#xff1a;Hello大家好呀&#xff0c;我是陈童学&#xff0c;一个与你一样正在慢慢前行的人。 博主主页&#xff1a;陈童学哦 所属专栏&#xff1a;CSTL 前言&#xff1a;Hello各位小伙伴们好&#xff01;欢迎来到本专栏CSTL的学习&#xff0c;本专栏旨在帮助大家了解…

接口测试五个重要测试点

一、功能测试 接口的功能是否实现、接口是否按照设计文档实现&#xff08;如&#xff1a;username参数写成了user&#xff09;———接口文档是在整个开发中使用&#xff0c;所以接口设计要与接口文档的设计保持一致。 1、兼容性测试&#xff1a;如&#xff1a;接口进行了调整…

【ACM训练】2023 河南 CCPC省赛 vp

2023 河南 CCPC省赛 题目链接 VP赛况&#xff1a; 目录 2023 河南 CCPC省赛赛况及总结赛况总结 补题 赛况及总结 赛况 开场&#xff1a;我提前打印了题册&#xff0c;于是我们开始分开看题目&#xff0c;我先看了A&#xff0c;发现很签&#xff0c;遂上机&#xff0c;8 m…

【软考系统规划与管理师笔记】第1篇 信息系统综合知识

目录 写在前面 1. 信息的基本概念 2. 信息的定量描述 3. 信息的传输模型 3. 信息系统的主要性能指标 4. 信息化的层次 5. 电子政务和电子商务 6. 信息系统 6.1 系统开发的方法 6.2 信息系统总体规划 6.3 信息系统规划内容 7 IT战略 8 习题收集 写在前面 系统规划与…