【Spring 】了解Spring AOP

目录

一、什么是Spring AOP

二、AOP的使用场景

三、AOP组成

四、Spring AOP的实现

1、添加Spring AOP依赖

2、定义切面和切点

3、定义相关通知

五、 AOP的实现原理

1、什么是动态代理 

2、 JDK代理和CGLIB代理的区别


一、什么是Spring AOP

AOP(Aspect Oriented Programming),直译过来就是面向切面编程,AOP是一种编程思想,是面向对象编程(OOP)的一种补充。Spring AOP是AOP思想的一种实现,就像DI一样是IoC的一种实现。

AOP的主要作用就是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点,减少对业务代码的侵入。增强代码的可读性和可维护性简单来所,AOP的作用就是保证开发者在不修改业务代码的前提下,位系统中的业务组件添加某种通用功能。

就比如实现一个用户登录权限的校验功能,就比如我们使用的博客,在要进入博客编辑的页面时,需要对你是否登录进行校验,如果已经登录,那么就可以进入编辑页,如果没有那么就需要在登录页面登录之后在进入。像这样需要登录校验的页面,我们使用AOP思想,只需要在某一处配置以下,所有的需要判断用户登录的页面就可以实现用户登录验证了。这样每个页面就只关注具体的业务逻辑了。

二、AOP的使用场景

就像上面举的例子,当你的程序中实现的页面越来越多,那么你要 写的登录验证也越来越多,⽽这些⽅法⼜是相同的,这么多的⽅法就会代码修改和维护的成本我们对这种功能统一,并且使用地方较多的功能,就可以考虑使用AOP来统一处理。当然AOP可以使用的场景还有很多。

  • 统一日志记录
  • 统一方法执行时间统计
  • 统一的返回格式设置
  • 统一的异常处理
  • 事务的开启和提交等

如果没有使用AOP思想来写代码,用户发送的请求直接被业务代码控制层接收到,请求访问的页面来校验用户是否登录。使用了AOP思想的代码,用户发送方的请求被AOP这里的代码先进行登录校验,如果登录,将请求传给控制层,如果没有登录就会被拦截。

三、AOP组成

1️⃣切面(Aspect):表示当前AOP是针对那些事件做处理的,用来登录的还是记录日志的。切面就是通知和切点的结合,通知和切点共同定义了切面的全部内容,他是干什么的,什么时候在哪里执行。通常以类的形式表示。

2️⃣切点(Pointcut):表示定义具体规则。切点其实就是筛选出的连接点,一个类中的所有方法都是连接点,但又不全需要,会筛选出某些作为连接点作为切点,如果说通知定义了切面的动作后者执行时机的话,切点则定义了执行的地点。

3️⃣通知(Advice):AOP执行的具体方法。有的地方叫增强。

4️⃣连接点(Join point):就是有可能触发切点的所有点。应用执行过程中能够插入切面的一个点,这个点可以是方法调用时,异常抛出时,甚至修改字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。

四、Spring AOP的实现

1、添加Spring AOP依赖

在创建好的Spring Boot项目的pom.xml中添加Spring AOP的依赖,我们可以从中央仓库中下载

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

然后点击刷新,触发下载。

2、定义切面和切点

这里使用注解@Aspect表示定义切面,即UserAserAspect类为切面,使用@Component注解表示让切面随着框架的启动而启动,这样切面中的切点定义的拦截规则才能生效。

package com.example.demo.common;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;@Aspect//定义切面
@Component//让切面随着框架的启动而启动
public class UserAspect {//定义切点,@Pointcut注解的参数中定义了具体的拦截规则。参数中使用AspectJ表达式语法@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")public void pointcut(){}
}

上述代码中,pointcut方法为空方法,它不需要又方法体,此方法名就是起到一个"标识"的作用,标识下面的通知方法具体指的是那个切点。因为一个切面中有很多切点。

上述pointcut方法上添加的@Pointcut注解的参数中使用切点表达式定义了具体的拦截规则

execution(* com.example.demo.controller.UserController.*(..))

切点表达的意思是拦截UserContrller类中的所有方法其参数为任意参数并且返回值为任意类型的返回值。

  • execution表示的意思为执行,执行的是后面跟的()中的规则。
  • *表示的多个部分组成的,有修饰符和返回值类型。
  • com.example.demo.controller.UserController表示要拦截com.example.demo.controller包中的UserController类
  • 类后面跟的*表示UserController类中的所有方法。
  • ..表示的不定式传参

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

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

常见表达式示例

  • execution(* com.example.demo.User.*(..)):匹配User类中的所有方法。
  • execution(* com.example.demo.User+.*(..)):匹配该类的子类包括该类的所有方法
  • execution(* com.example.*.*(..)):匹配com.example包下的所有类的所有方法
  • execution(* com.example..*.*(..)):匹配com.example包下,子孙包下所有类的所有方法
  • execution(* addUser(String,int)):匹配addUser方法,其第一个参数类型是String,第二个参数类型是int。

创建UserController类,这个类中的方法哪一个要被执行(目标方法)哪一个就是连接点

package com.example.demo.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/getuser")public String getUser(){System.out.println("do getUser");return "get user";}@RequestMapping("/deluser")public String delUser(){System.out.println("do delUser");return "del user";}
}

3、定义相关通知

通知定义的是被拦截的方法具体要执行的业务。比如用户登录权限验证方法就是具体要执行的业务。

Spring AOP中,可以在方法上使用以下注解,会设置方法为通知方法,在满足条件后会通知本方法进行调用:

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

1️⃣前置通知和后置通知的实现

package com.example.demo.common;import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;@Aspect//定义切面
@Component//让切面随着框架的启动而启动
public class UserAspect {//定义切点,@Pointcut注解的参数中定义了具体的拦截规则@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")public void pointcut(){}//定义前置通知@Before("pointcut()")//表示这个通知是针对pointcut方法的public void doBefore(){System.out.println("执行了前置通知");}//定义后置通知@After("pointcut()")public void doAfter(){System.out.println("执行了后置通知");}
}

当我们在前端页面中访问UserController类的方法时,后端程序的控制台上每次出现的结果是先执行前置通知,在执行目标方法(连接点),然后执行后置通知。

 

2️⃣环绕通知的具体实现 

环绕通知方法是具有Object类型的返回值,需要把方法执行结果返回给框架,框架拿到对象继续执行。

package com.example.demo.common;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Aspect//定义切面
@Component//让切面随着框架的启动而启动
public class UserAspect {//定义切点,@Pointcut注解的参数中定义了具体的拦截规则@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")public void pointcut(){}//定义前置通知@Before("pointcut()")//表示这个通知是针对pointcut方法的public void doBefore(){System.out.println("执行了前置通知");}//定义后置通知@After("pointcut()")public void doAfter(){System.out.println("执行了后置通知");}//定义环绕通知@Around("pointcut()")//环绕通知方法的参数为要执行的连接点,也就是我们在前端访问的目标方法public Object doAround(ProceedingJoinPoint joinPoint){System.out.println("环绕通知之前");Object result = null;try {//执行目标方法,它的目标方法就是我们在前端访问的方法result = joinPoint.proceed();} catch (Throwable e) {e.printStackTrace();}System.out.println("环绕通知之后");return result;}
}

从执行结果中可以看到环绕通知的执行范围,可以环绕执行通知是最先执行的,然后是执行前置通知,然后再执行目标方法,然后执行后置通知,最后所有的方法执行完成了,环绕通知方法才会执行完成。

 

五、 AOP的实现原理

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

Spring AOP使用两种混合的实现方式:JDK动态代理和CGLib动态代理。

  • JDK动态代理:如果目标对象实现了InvocationHandler接口,Spring将使用JDK动态代理来创建代理对象。
  • CGLib动态代理:如果目标对象没有实现InvocationHandler接口,Spring将使用CGLib代理,通过继承目标对象来创建代理对象。

1、什么是动态代理 

代理可以看作是对调用目标的一个包装,这样我们对目标代理的调用不是直接发生的,而是通过代理完成。

当想要给实现了某个接口的类中的方法,加一些额外的处理,比如加日志,加事务等。可以给这个类创建一个代理,也就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新方法,这个代理类并不是定义好的,而是动态生成的,具有解耦意义,灵活、扩展性强。

在Java中,动态代理通常使用Java.lang.reflect.Proxy类和Java.lang.reflect.InvocationHandler接口来实现。

2、 JDK代理和CGLIB代理的区别

  • 接口要求:JDK动态代理只能对实现了接口的类生成代理;而CGLIB代理可以没有实现接口的类,是通过继承被代理类,在运行时动态的生成代理对象。
  • 生成方式:JDK代理使用Java的反射机制来完成代理对象,而CGLIB代理使用CGLIB库生成代理对象,通过修改目标类的字节码来实现。所以该类不能被final修饰

如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
 

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

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

相关文章

HarmonyOS NEXT新能力,一站式高效开发HarmonyOS应用

2023年8月6日华为开发者大会2023&#xff08;HDC.Together&#xff09;圆满收官&#xff0c;伴随着HarmonyOS 4的发布&#xff0c;华为向开发者发布了汇聚所有最新开发能力的HarmonyOS NEXT开发者预览版&#xff0c;并分享了围绕“一次开发&#xff0c;多端部署” “可分可合&a…

Spring Boot业务代码中使用@Transactional事务失效踩坑点总结

1.概述 接着之前我们对Spring AOP以及基于AOP实现事务控制的上文&#xff0c;今天我们来看看平时在项目业务开发中使用声明式事务Transactional的失效场景&#xff0c;并分析其失效原因&#xff0c;从而帮助开发人员尽量避免踩坑。 我们知道 Spring 声明式事务功能提供了极其…

面试之快速学习STL-deuqe和list

1. deque deque 容器用数组&#xff08;数组名假设为 map&#xff09;存储着各个连续空间的首地址。也就是说&#xff0c;map 数组中存储的都是指针如果 map 数组满了怎么办&#xff1f;很简单&#xff0c;再申请一块更大的连续空间供 map 数组使用&#xff0c;将原有数据&…

css3-grid:grid 布局 / 基础使用

一、理解 grid 二、理解 css grid 布局 CSS Grid布局是一个二维的布局系统&#xff0c;它允许我们通过定义网格和网格中每个元素的位置和尺寸来进行页面布局。CSS Grid是一个非常强大的布局系统&#xff0c;它不仅可以用于构建网格布局&#xff0c;还可以用于定位元素&#xf…

IC流程中 DFT 学习笔记(1)

引言 DFT是ASIC芯片设计流程中不可或缺的环节。其主要目的是在芯片前端设计验证完成后插入一些诸如寄存器链等可供测试的逻辑&#xff0c;算是IC后端设计的范畴。主要是在ASIC芯片流片完成后&#xff0c;通过这些已插入的逻辑&#xff0c;检测流片得到的芯片的制造质量。检测一…

Flink之Partitioner(分区规则)

Flink之Partitioner(分区规则) 方法注释global()全部发往1个taskbroadcast()广播(前面的文章讲解过,这里不做阐述)forward()上下游并行度一致时一对一发送,和同一个算子连中算子的OneToOne是一回事shuffle()随机分配(只是随机,同Spark的shuffle不同)rebalance()轮询分配,默认机…

玩转VS code 之 C/C++ 环境配置篇

PS&#xff1a;俺是菜鸟&#xff0c;整理和踩坑试错花了不少时间&#xff0c;如果这篇文章对您有用的话&#xff0c;请麻烦您留下免费的赞赞&#xff0c;赠人玫瑰&#xff0c;手留余香&#xff0c;码字踩坑不易&#xff0c;望三连支持 上一篇&#xff1a;玩转 VS code 之下载篇…

激活函数总结(十):激活函数补充(Identity、LogSigmoid、Bent Identity)

激活函数总结&#xff08;十&#xff09;&#xff1a;激活函数补充 1 引言2 激活函数2.1 Identity激活函数2.2 LogSigmoid激活函数2.3 Bent Identity激活函数 3. 总结 1 引言 在前面的文章中已经介绍了介绍了一系列激活函数 (Sigmoid、Tanh、ReLU、Leaky ReLU、PReLU、Swish、…

【Spring系列篇--关于IOC的详解】

目录 面试经典题目&#xff1a; 1. 什么是spring&#xff1f;你对Spring的理解&#xff1f;简单来说&#xff0c;Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。 2.什么是IoC&#xff1f;你对IoC的理解&#xff1f;IoC的重要性?将实例化对象的权利从程序员…

Rust软件外包开发语言的特点

Rust 是一种系统级编程语言&#xff0c;强调性能、安全性和并发性的编程语言&#xff0c;适用于广泛的应用领域&#xff0c;特别是那些需要高度可靠性和高性能的场景。下面和大家分享 Rust 语言的一些主要特点以及适用的场合&#xff0c;希望对大家有所帮助。北京木奇移动技术有…

Windows上使用FFmpeg实现本地视频推送模拟海康协议rtsp视频流

场景 Nginx搭建RTMP服务器FFmpeg实现海康威视摄像头预览&#xff1a; Nginx搭建RTMP服务器FFmpeg实现海康威视摄像头预览_nginx rtmp 海康摄像头_霸道流氓气质的博客-CSDN博客 上面记录的是使用FFmpeg拉取海康协议摄像头的rtsp流并推流到流媒体服务器。 如果在其它业务场景…

【Axure高保真原型】JS日期选择器筛选中继器表格

今天和大家分享JS日期选择器筛选中继器表格的原型模板&#xff0c;通过调用浏览器的日期选择器&#xff0c;所以可以获取真实的日历效果&#xff0c;具体包括哪一年二月份有29天&#xff0c;几号对应星期几&#xff0c;都是真实的&#xff0c;获取日期值后&#xff0c;通过交互…

Unity C# 之 Azure 微软SSML语音合成TTS流式获取音频数据以及表情嘴型 Animation 的简单整理

Unity C# 之 Azure 微软SSML语音合成TTS流式获取音频数据以及表情嘴型 Animation 的简单整理 目录 Unity C# 之 Azure 微软SSML语音合成TTS流式获取音频数据以及表情嘴型 Animation 的简单整理 一、简单介绍 二、实现原理 三、注意事项 四、实现步骤 五、关键代码 一、简…

JVS开源基础框架:平台基本信息介绍

JVS是面向软件开发团队可以快速实现应用的基础开发脚手架&#xff0c;主要定位于企业信息化通用底座&#xff0c;采用微服务分布式框架&#xff0c;提供丰富的基础功能&#xff0c;集成众多业务引擎&#xff0c;它灵活性强&#xff0c;界面化配置对开发者友好&#xff0c;底层容…

嵌入式:ARM Day4

一、自己编写代码实现三盏灯点亮 源码&#xff1a; .text .global _start _start: 进行一次初始化bl RCC_INITbl LED1_INITbl LED2_INITbl LED3_INITb looploop: 循环开关灯bl LED1_ONbl delay_1sbl LED1_OFFbl delay_1sbl LED2_ONbl delay_1sbl LED2_OFFbl delay_1sbl…

kafka线上问题优化

如何防止消息丢失 生产者&#xff1a; 使用同步发送把ack设成1或者all&#xff08;非0&#xff0c;0可能会出现消息丢失的情况&#xff09;&#xff0c;并且设置同步的分区数>2 消费者&#xff1a;把自动提交改成手动提交 如何防止重复消费 在防止消息丢失的方案中&#…

每天一道leetcode:1926. 迷宫中离入口最近的出口(图论中等广度优先遍历)

今日份题目&#xff1a; 给你一个 m x n 的迷宫矩阵 maze &#xff08;下标从 0 开始&#xff09;&#xff0c;矩阵中有空格子&#xff08;用 . 表示&#xff09;和墙&#xff08;用 表示&#xff09;。同时给你迷宫的入口 entrance &#xff0c;用 entrance [entrancerow, …

配置vscode

配置vscode 设置相关 网址&#xff1a;https://code.visualstudio.com/ 搜索不要用百度用这个&#xff1a;cn.bing.com 1.安装中文包 Chinese (Simplified) (简体中文) 2.安装 open in browser 3.安装主题 Atom One Dark Theme 4. 安装图标样式 VSCode Great Icons 5.安装 L…

使用navicat连接postgresql报错问题解决

使用navicat连接postgresql报错问题解决 一、问题现象&#xff1a; 最近使用Navicat来连接postgreSQL数据库&#xff0c;发现连接不上&#xff0c;报错信息如下&#xff1a; 自己百度了一下&#xff0c;发现pgsql 15版本以后&#xff0c;有些系统表的列名改了&#xff0c;pg_…

HTTPS 的加密流程

目录 一、HTTPS是什么&#xff1f; 二、为什么要加密 三、"加密" 是什么 四、HTTPS 的工作过程 1.对称加密 2.非对称加密 3.中间人攻击 4.证书 总结 一、HTTPS是什么&#xff1f; HTTPS (Hyper Text Transfer Protocol Secure) 是基于 HTTP 协议之上的安全协议&…