[详解]Spring AOP

news2024/11/23 10:04:45

 

  • 🎥 个人主页:Dikz12
  • 🔥个人专栏:Spring学习之路
  • 📕格言:吾愚多不敏,而愿加学
  • 欢迎大家👍点赞✍评论⭐收藏

目录

什么是AOP? 

Spring AOP 快速入门

Spring AOP核心概念

切点(Pointcut)

连接点(Join Point) 

通知(Advice)

切⾯(Aspect)  

 通知类型

切点表达式

 execution表达式

@annotation  

Spring AOP 的实现方式

Spring AOP 原理 

代理模式 

动态代理 

CGLIB动态代理实现

 总结


什么是AOP? 

Aspect Oriented Programming(⾯向切⾯编程)

什么是⾯向切⾯编程呢?
切⾯就是指某⼀类特定问题, 所以AOP也可以理解为⾯向特定⽅法编程.
什么是⾯向特定⽅法编程呢?
就是⼀类特定问题. 登录校验拦截器, 就 是对"登录校验"这类问题的统⼀处理. 所以, 拦截器也是AOP的⼀种应⽤.
简单来说: AOP是⼀种思想, 是对某⼀类事情的集中处理.Spring对AOP进行了实现,并提供了一些API,就是Spring AOP.  比如:统一数据格式 和 统一异常处理,也是AOP的一种实现.

Spring AOP 快速入门

实现记录Controller方法每个方法执行花费的时间.

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Slf4j
@Aspect
@Component
public class TimeAspect {
    /**
     * 查看每个接口所需的时间
     */
    @Around("execution(* com.example.demo.controller.*.*(..))")
    public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("方法执行前");
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long end = System.currentTimeMillis();
        log.info("方法执行后");
        log.info(joinPoint+ "消耗时间" +(end-start)+ "ms");
        return result;
    }
}

运行效果: 

对程序代码进⾏简单的讲解:
  1.  @Aspect: 标识这是⼀个切⾯类
  2.  @Around: 环绕通知, 在⽬标⽅法的前后都会被执⾏. 后⾯的表达式表⽰对哪些⽅法进⾏增强.
  3. ProceedingJoinPoint.proceed() 让原始⽅法执⾏

 整个代码划分为三部分:

通过上⾯的程序, 我们也可以感受到AOP⾯向切⾯编程的⼀些 优势
  • 代码⽆侵⼊: 不修改原始的业务⽅法, 就可以对原始的业务⽅法进⾏了功能的增强或者是功能的改变
  • 减少了重复代码
  • 提⾼开发效率
  • 维护⽅便

Spring AOP核心概念

切点(Pointcut)

切点(Pointcut), 也称之为"切⼊点"
Pointcut 的作⽤就是提供⼀组规则 (使⽤ AspectJ pointcut expression language 来描述), 告诉程序对 哪些⽅法来进⾏功能增强.

连接点(Join Point) 

满⾜切点表达式规则的⽅法 , 就是连接点. 也就是可以被AOP控制的⽅法
以⼊⻔程序举例, 所有 com.example.demo.controller 路径下的⽅法, 都是连接点。
package com.example.demo.controller;
@RequestMapping("/book")
@RestController
public class BookController {
   @RequestMapping("/addBook")
   public Result addBook(BookInfo bookInfo) {
    //...代码省略
   }
    @RequestMapping("/queryBookById")
   public BookInfo queryBookById(Integer bookId){
   //...代码省略
  }
}
上述BookController 中的⽅法都是连接点。(也就是目标方法)
连接点是满⾜切点表达式的元素. 切点可以看做是保存了众多连接点的⼀个集合.
⽐如:
切点表达式: 学校全体教师
连接点就是: 张三,李四等各个⽼师

通知(Advice)

通知就是具体要做的⼯作, 指哪些重复的逻辑,也就是共性功能(最终体现为⼀个⽅法)
⽐如上述程序中记录业务⽅法的耗时时间, 就是通知。

切⾯(Aspect)  

切⾯(Aspect) = 切点(Pointcut) + 通知(Advice)
通过切⾯就能够描述当前AOP程序需要针对于哪些⽅法, 在什么时候执⾏什么样的操作.
切⾯既包含了通知逻辑的定义, 也包括了连接点的定义。
切⾯所在的类, 我们⼀般称为切⾯类(被@Aspect注解标识的类)

 通知类型

Spring中AOP的通知类型有以下⼏种: 

  •  @Around:环绕通知,此注解标注的通知⽅法在⽬标⽅法前,后都被执⾏.
  •  @Before:前置通知,此注解标注的通知⽅法在⽬标⽅法前被执⾏.
  • @After:后置通知,此注解标注的通知⽅法在⽬标⽅法后被执⾏,⽆论是否有异常都会执⾏.
  • @AfterReturning:返回后通知,此注解标注的通知⽅法在⽬标⽅法后被执⾏,有异常不会执⾏.
  • @AfterThrowing:异常后通知,此注解标注的通知⽅法发⽣异常后执⾏.

执行顺序: 

程序发⽣异常的情况下: 

切点表达式

上⾯的代码中, 我们⼀直在使⽤切点表达式来描述切点. 下⾯我们来介绍⼀下切点表达式的语法.
切点表达式常⻅有两种表达⽅式
  1. execution(RR):根据⽅法的签名来匹配
  2. @annotation(RR) :根据注解匹配

 execution表达式

execution(<访问修饰符> <返回类型> <包名.类名.⽅法(⽅法参数)> <异常>)

切点表达式⽀持通配符表达:
1. * :匹配任意字符,只匹配⼀个元素(返回类型, 包, 类名, ⽅法或者⽅法参数)
     a. 包名使⽤ * 表⽰任意包(⼀层包使⽤⼀个*)
     b. 类名使⽤ * 表⽰任意类
     c. 返回值使⽤ * 表⽰任意返回值类型
     d. ⽅法名使⽤ * 表⽰任意⽅法
     e. 参数使⽤ * 表⽰⼀个任意类型的参数
2. .. :匹配多个连续的任意符号, 可以通配任意层级的包, 或任意类型, 任意个数的参数
     a. 使⽤ .. 配置包名,标识此包以及此包下的所有⼦包
     b. 可以使⽤ .. 配置参数,任意个任意类型的参数

@annotation  

实现步骤:
  1. 编写⾃定义注解
  2. 使⽤ @annotation 表达式来描述切点
  3. 在连接点的⽅法上添加⾃定义注解

1.⾃定义注解 @MyAspect 

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {

}

 2.使⽤ @annotation 切点表达式定义切点

@RestController
@Slf4j
@Aspect
public class MyAspectDemo {
    @Before("@annotation(com.example.demo.aspect.MyAspect)")
    public void doBefore() {
        log.info("MyAspect -> Before ...");

    }
   @After("@annotation(com.example.demo.aspect.MyAspect)")
    public void doAfter() {
       log.info("MyAspect -> After ...");
   }
}

3.添加⾃定义注解  

public class TestController {
    @MyAspect
    @RequestMapping("/t2")
    public String t2(){
        return "t2";
    }

}

Spring AOP 的实现方式

  1. 基于注解 @Aspect (最常用)
  2.  基于⾃定义注解 (参考⾃定义注解 @annotation 部分的内容)
  3.  基于Spring API (通过xml配置的⽅式, ⾃从SpringBoot ⼴泛使⽤之后, 这种⽅法⼏乎看不到了, 课 下⾃⼰了解下即可)
  4. 基于代理来实现(更加久远的⼀种实现⽅式, 写法笨重, 不建议使⽤

Spring AOP 原理 

Spring AOP的原理, 也就是Spring 是如何实 现AOP的.
Spring AOP 是基于动态代理实现的.(也就是代理模式中的动态代理)

代理模式 

在某些情况下, ⼀个对象不适合或者不能直接引⽤另⼀个对象, ⽽代理对象可以在客⼾端和⽬标对象之 间起到中介的作⽤.
使⽤代理前:

使用代理后: 

 

 生活中的代理:

  • 艺⼈经纪⼈: ⼴告商找艺⼈拍⼴告, 需要经过经纪⼈,由经纪⼈来和艺⼈进⾏沟通.
  • 房屋中介: 房屋进⾏租赁时, 卖⽅会把房屋授权给中介, 由中介来代理看房, 房屋咨询等服务.
  • 经销商: ⼚商不直接对外销售产品, 由经销商负责代理销售.
静态代理: 由程序员创建代理类或特定⼯具⾃动⽣成源代码再对其编译, 在程序运⾏前代理类的
.class ⽂件就已经存在了。 (在程序运行前,代理对象就应经对目标对象进行了步骤的预执行代码)代码写死了.

动态代理 

动态代理: 在程序运⾏时, 运⽤反射机制动态创建⽽成。(不需要针对每个目标对象都单独创建一个代理对象)

 Java也对动态代理进⾏了实现, 并给我们提供了⼀些API, 常⻅的实现⽅式有两种:

  1. JDK动态代理
  2. CGLIB动态代理

CGLIB动态代理实现

实现步骤: 

  1. 定义⼀个类(被代理类)
  2. ⾃定义 MethodInterceptor 并重写 intercept ⽅法, intercept ⽤于增强⽬标⽅ 法,和 JDK 动态代理中的 invoke ⽅法类似
  3. 通过 Enhancer 类的 create()创建代理类

⾃定义 MethodInterceptor 

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLIBInterceptor implements MethodInterceptor {
 //⽬标对象, 即被代理对象
 private Object target;
 public CGLIBInterceptor(Object target){
     this.target = target;
 }
 @Override
 public Object intercept(Object o, Method method, Object[] objects, 
     MethodProxy methodProxy) throws Throwable {
   // 代理增强内容
    System.out.println("我是中介, 开始代理");
   //通过反射调⽤被代理类的⽅法
   Object retVal = methodProxy.invoke(target, objects);
    //代理增强内容
   System.out.println("我是中介, 代理结束");
   return retVal;
 }
}

 创建代理类, 并使⽤

public class DynamicMain {
 public static void main(String[] args) {
    HouseSubject target= new RealHouseSubject();
    HouseSubject proxy= (HouseSubject) 
    Enhancer.create(target.getClass(),new CGLIBInterceptor(target));
    proxy.rentHouse();
 }
}

Spring AOP 源码解析就不在进行详细解析,这东西了解就行了。

Spring对于AOP的实现,基本上都是靠 AnnotationAwareAspectJAutoProxyCreator 去完成
⽣成代理对象的逻辑在 ⽗类 AbstractAutoProxyCreator 中。

 总结

  1. AOP是⼀种思想, 是对某⼀类事情的集中处理. Spring框架实现了AOP, 称之为SpringAOP。
  2.  Spring AOP常⻅实现⽅式有两种: 1. 基于注解@Aspect来实现 2. 基于⾃定义注解来实现, 还有⼀些 更原始的⽅式,⽐如基于代理, 基于xml配置的⽅式, 但⽬标⽐较少⻅
  3. Spring AOP 是基于动态代理实现的, 有两种⽅式: 1. 基本JDK动态代理实现 2. 基于CGLIB动态代理 JDK动态代理只能代理接口,CGLIB既可以代理接口,也可以代理类. 如果是类的话一定是CGLIB代理的.  

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

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

相关文章

普乐蛙元宇宙VR体验馆设备集体亮相VR文旅景区展

普乐蛙全国巡展又双叒叕开始了! 这次来到的是“好客山东”↓↓ 山东2024休闲旅游产业展 4月25日至27日&#xff0c;2024休闲旅游产业展在临沂国际博览中心举办。本次展会以“潮购文旅好品&#xff0c;乐享时尚生活”为主题&#xff0c;汇聚全国文旅产业上下游500多家企业、上万…

SkyWalking 自定义Span并接入告警

图容易被CSDN吞掉&#xff0c;我在掘金也发了&#xff1a;https://juejin.cn/post/7361821913398837248 我就是这么膨胀 最近在做 OpenAI API 套壳&#xff0c;当我使用 okhttp-sse 这个库进行流式内容转发的时候&#xff0c;我发现有些回调方法 SkyWalking 不能抓取到。这就…

Java---数据类型与变量

1.字面常量 字面常量就是我们经常所说的常量&#xff0c;常量即在程序运行期间&#xff0c;固定不变的量。且常量是无法改变的&#xff0c;如果我们的代码有改变常量的操作&#xff0c;程序就会报错。 1.1字面常量的分类 字符串常量&#xff0c;整型常量&#xff0c;浮点数常…

Windows命令行基本命令

目录 什么是相对路径和绝对路径&#xff1f; 一、目录&#xff08;文件夹&#xff09;和文件操作 1.cd命令 用于切换目录 2.dir命令 用于显示目录和文件列表 3.md或mkdir命令 创建文件&#xff0c;也可以创建多级子目录 4.rd命令 用于删除目录 5.move命令 用于移动…

C++|STL-list运用(1)

cplusplus.com/reference/list/list/?kwlist list介绍 list是一个双向循环链表&#xff0c;双向循环链表它的每个节点都有两个链接&#xff0c;一个指向前一个节点&#xff0c;另一个指向下一个节点&#xff0c;且最后一个结点指向头节点。 结点组成 1.数据域 2.指针域 &a…

基于SpringBoot+Vue高校宣讲会管理系统设计与实现

项目介绍&#xff1a; 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装高校宣讲会管理系统软件来发挥其高效地信息…

请编写函数fun,该函数的功能是:将M行N列的二维数组中的数据,按行的顺序依 次放到一维数组中,一维数组中数据的个数存放在形参n所指的存储单元中。

本文收录于专栏:算法之翼 https://blog.csdn.net/weixin_52908342/category_10943144.html 订阅后本专栏全部文章可见。 本文含有题目的题干、解题思路、解题思路、解题代码、代码解析。本文分别包含C语言、C++、Java、Python四种语言的解法完整代码和详细的解析。 题干 请编…

2024年五大企业邮箱最新排名:价格与服务全面对比

在选择企业邮箱时&#xff0c;我们都需要对比企业邮箱价格和邮箱服务。国内五大企业邮箱有Zoho Mail、新浪、网易、腾讯、阿里&#xff0c;这些企业邮箱功能各有偏重点&#xff0c;价格也不一&#xff0c;到底排名如何&#xff1f;我们今天来进行个价格和服务的全面对比。 一、…

家政行业赋能链动:商业模式创新开启全新篇章

大家好&#xff0c;我是微三云周丽&#xff01; 在当今娱乐行业蓬勃发展的背景下&#xff0c;越来越多的年轻人对卫生打理的需求逐渐增加&#xff0c;同时也催生了家政行业的兴起。 然而&#xff0c;如何在激烈的竞争中脱颖而出&#xff0c;成为家政行业面临的重要挑战。本文…

【智能算法】火烈鸟搜索算法(FSA)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2021年&#xff0c;W Zhiheng等人受到火烈鸟迁徙觅食行为启发&#xff0c;提出了火烈鸟搜索算法&#xff08;Flamingo Search Algorithm, FSA&#xff09;。 2.算法原理 2.1算法思想 FSA受到火烈鸟…

【unity】三维数学应用(计算线和面的交点)

【unity】三维数学应用&#xff08;计算线和面的交点&#xff09; 实现方法有多种&#xff0c;下面介绍一种简单的方法。利用一个点指向面上任意点的向量&#xff0c;到该面法线的投影长度相同的基本原理&#xff0c;结合相似三角形既可以求出交点。 原理 如下图 GD组成的线段…

win11 Windows ADK制作的win pe中没有manage-ade命令或命令无法正常工作解决办法

解决办法 不使用win pe&#xff0c;而是使用Windows安装程序。 将iso镜像烧录到u盘&#xff0c;然后从它引导。 按shift f10弹出cmd&#xff0c;里面存在manage-bde且正常工作。 其他 win pe找不到命令manage-ade 在win pe上使用manage-ade需要一个包 a&#xff0c;adk制作…

计算机网络 备查

OSI 七层模型 七层模型协议各层实现的功能 简要 详细 TCP/IP协议 组成 1.传输层协议 TCP 2.网络层协议 IP 协议数据单元&#xff08;PDU&#xff09;和 封装 数据收发过程 数据发送过程 1. 2.终端用户生成数据 3.数据被分段&#xff0c;并加上TCP头 4.网络层添加IP地址信息…

练习题(2024/4/29)

在深度优先遍历中&#xff1a;有三个顺序&#xff0c;前中后序遍历 这里前中后&#xff0c;其实指的就是中间节点的遍历顺序&#xff0c;只要记住 前中后序指的就是中间节点的位置就可以了。 如图 1二叉树的前序遍历 给你二叉树的根节点 root &#xff0c;返回它节点值的 前…

Vue3+Nuxt3 从0到1搭建官网项目(SEO搜索、中英文切换、图片懒加载)

Vue2Nuxt2 从 0 到1 搭建官网~ 想开发一个官网&#xff0c;并且支持SEO搜索&#xff0c;当然离不开我们的 Nuxt &#xff0c;Nuxt2 我们刚刚可以熟练运用&#xff0c;现在有出现了Nuxt3&#xff0c;那通过本篇文章让我们一起了解一下。 安装 Nuxt3 // npx nuxilatest init &…

乐观锁悲观锁

视频&#xff1a;什么是乐观锁&#xff1f;什么是悲观锁&#xff1f;_哔哩哔哩_bilibili

Leetcode—2739. 总行驶距离【简单】

2024每日刷题&#xff08;121&#xff09; Leetcode—2739. 总行驶距离 实现代码 class Solution { public:int distanceTraveled(int mainTank, int additionalTank) {int consume 0;int ans 0;while(mainTank ! 0) {mainTank--;consume;if(consume 5 && additio…

数据分析案例-全球表面温度数据可视化与统计分析

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

基于SpringBoot+Vue高校汉服租赁网站的设计与实现

项目介绍&#xff1a; 高校汉服租赁网站管理系统按照操作主体分为管理员和用户。管理员的功能包括字典管理、交流论坛管理、公告资讯管理、汉服信息管理、汉服收藏管理、汉服评价管理、汉服租赁管理、用户管理、管理员管理。用户的功能等。该系统采用了Mysql数据库&#xff0c…

03 后端入参校验:自定义注解实现

03 后端入参校验&#xff1a;自定义注解实现 一、前言二、实现1、新建Spring Boot项目2、引入依赖3、新建注解类4、新建校验器5、全局异常处理器6、编写Controller7、新建实体类8、启动并测试 一、前言 在 Java 后端开发中&#xff0c;为了实现入参校验&#xff0c;常常会使用…