【SpringAOP】Spring AOP 底层逻辑:切点表达式与原理简明阐述

前言

🌟🌟本期讲解关于spring aop的切面表达式和自身实现原理介绍~~~

🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客

🔥 你的点赞就是小编不断更新的最大动力                                       

🎆那么废话不多说直接开整吧~~

目录

📚️1.切点表达式

🚀1.1execution表达式

 🚀1.2@annotation

1.自定义注解

2.切面类

3.添加注解

📚️2.Spring AOP原理

🚀2.1代理模式

1.静态代理(了解)

2.动态代理(八股)

🚀2.2Spring AOP原理总结

📚️3.总结

📚️1.切点表达式

我们在上一期了解到了关于通知中切点表达式execution表达式,但是没有做很详细的介绍,不仅如此在切点表达式中,有两种方式,下面来一一介绍

🚀1.1execution表达式

切点表达式的结构如下所示:

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

而在我们上期写的表达式中格式如下:

@Around("execution(* com.example.springaop.controller.*.*(..))")

 解释:

* :匹配任意字符,只匹配⼀个元素(返回类型, 包, 类名, ⽅法或者⽅法参数)

.. :匹配多个连续的任意符号, 可以通配任意层级的包, 或任意类型, 任意个数的参数

上述的controller后的两个 * 代表就是每一层,括号里的 .. 就是表示任意的方法参数;这里也可以代表无参数;

举例:

 execution(public String com.example.demo.controller.TestController.t1())

 解释:testcontroller类下的名为t1的方法,并且这里的方法是无参的,访问修饰符可以省去;

 execution(* com.example.demo.controller.TestController.t1())

解释:这里可以访问的就是没有任何限制的访问权限,匹配所有返回类型;

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

解释:这里匹配所有的方法,这些方法是无参的;

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

解释:这里匹配controller包下所有类的所有方法

 execution(* com..TestController.*(..))

解释:这里匹配com包下的所有testcontroller类的所有方法 

 🚀1.2@annotation

我们在上述的简述中了解到,关于execution表达式如何作用于匹配某个方法和类,但是当存在两个类,都要进行匹配,并且里面的方法也是无规则的,那如何呢,此时就要@annotation进行操作了;

假如有以下的两个控制类:

@RestController
@RequestMapping("/test")
public class TestController {@RequestMapping("/t1")public String test1(){log.info("这是t1执行");return "t1";}@RequestMapping("/t2")public String test2(){log.info("这是t2在执行");return "t2";}
}
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/u1")public String user1(){return "u1";}@RequestMapping("/u2")public String user2(){return "u2";}
}

 那么此时我们要将第一个控制器类的第一个方法进行匹配,以及第二个控制器类的方法进行匹配,那么此时的操作就是如下所示:

1.自定义注解

代码如下所示:

//定义自己的注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {
}

@Target 标识了 Annotation 所修饰的对象范围, 即该注解可以⽤在什么地⽅.

@Retention 指Annotation被保留的时间⻓短, 标明注解的⽣命周期

2.切面类

使⽤ @annotation 切点表达式定义切点, 只对 @MyAspect ⽣效

代码如下所示:

@Aspect
@Component
@Slf4j
public class MyAnnotationAspect {@Before("@annotation(com.example.springaop.config.MyAspect)")public void doBefore(){log.info("before start...");}@After("@annotation(com.example.springaop.config.MyAspect)")public void doAfter(){log.info("after start...");}
}

这里小编设置了两种通知类,第一种是在目标方法执行前进行执行,第二种通知是在方法执行后进行执行,当然表达式里的的是我们自己注解的全限定路径,加上我们的定义注解名称;

3.添加注解

代码如下所示:

    @MyAspect@RequestMapping("/t1")public String test1(){log.info("这是t1执行");return "t1";}

这是第一个类的第一个方法;

@MyAspect@RequestMapping("/u2")public String user2(){return "u2";}

这是第二个类的第二个方法;

此时就与这两个方法进行了匹配,那么就可执行切面通知了;

📚️2.Spring AOP原理

🚀2.1代理模式

代理模式也叫委托模式定义:为其他对象提供⼀种代理以控制对这个对象的访问. 它的作⽤就是通过提供⼀个代理类, 让我们在调⽤⽬标⽅法的时候, 不再是直接对⽬标⽅法进⾏调⽤, ⽽是通过代理类间接调⽤(其实就是中介的作用)

1. Subject: 业务接⼝类. 可以是抽象类或者接⼝(不⼀定有)
2. RealSubject: 业务实现类. 具体的业务执⾏, 也就是被代理对象.(真正的房东
3. Proxy: 代理类. RealSubject的代理 (中介

这里的代理模式分为两种

• 静态代理: 由程序员创建代理类或特定⼯具⾃动⽣成源代码再对其编译, 在程序运⾏前代理类的
.class ⽂件就已经存在了.
• 动态代理: 在程序运⾏时, 运⽤反射机制动态创建⽽成 

1.静态代理(了解)

代码如下:

定义一个subject业务接口;

public interface SubjectHouse {void rentHouse();
}

让代理对象继承这个接口,并写出要代理什么

public class RealSubjectHouse implements SubjectHouse{//真正的房东@Overridepublic void rentHouse(){System.out.println("我是房东,我要出租房子");}
}

最后中介,进行代理

public class HouseProxy implements SubjectHouse{private SubjectHouse subjectHouse;public HouseProxy(SubjectHouse subjectHouse) {this.subjectHouse = subjectHouse;}@Overridepublic void rentHouse() {System.out.println("我是中介开始代理");subjectHouse.rentHouse();System.out.println("我是中介结束代理");}
}

解释这里使用了一个向上转型的思想,这里实际指定就是代理的对象就是realsubjecthouse(即真正的房东);

从上述代码可以看出, 我们修改接⼝(Subject)和业务实现类(RealSubject)时, 还需要修改代理类
(Proxy).
同样的, 如果有新增接⼝(Subject)和业务实现类(RealSubject), 也需要对每⼀个业务实现类新增代理类(Proxy).

2.动态代理(八股)

相⽐于静态代理来说,动态代理更加灵活.
我们不需要针对每个⽬标对象都单独创建⼀个代理对象, ⽽是把这个创建代理对象的⼯作推迟到程序运
⾏时由JVM来实现. 也就是说动态代理在程序运⾏时, 根据需要动态创建⽣成

这里的动态代理分为两种

JDK动态代理;

CGLIB动态代理;

 JDK动态代理代码(了解)

public class JDKInvocationHandler implements InvocationHandler {private Object target;//目标对象public JDKInvocationHandler(Object target) {this.target = target;}/*** 代理对象  通过 invoke调用  目标对象的方法*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("我是代理, 开始代理");//通过反射, 调用目标对象的方法Object result = method.invoke(target, args);System.out.println("我是代理, 结束代理");return result;}
}

进行main方法调用:

SubjectHouse proxy= (SubjectHouse) Proxy.newProxyInstance(SubjectHouse .class.getClassLoader(),new Class[]{SubjectHouse .class},new JDKInvocationHandler(target));proxy.rentHouse();

newProxyInstance() , 这个⽅法主要⽤来⽣成⼀个代理对象

Loader: 类加载器, ⽤于加载代理对象.
interfaces : 被代理类实现的⼀些接⼝(这个参数的定义, 也决定了JDK动态代理只能代理实现了接⼝的⼀些类)
h : 实现了 InvocationHandler 接⼝的对象,target被代理对象

CGLIB动态代理代码

添加依赖:

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
public class CGLibMethodInterceptor implements MethodInterceptor {private Object target;public CGLibMethodInterceptor(Object target) {this.target = target;}/*** 调用代理对象的方法*/@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("我是中介, 开始代理");Object result = method.invoke(target, args);System.out.println("我是中介, 结束代理");return result;}
}

最后进行调用:

  SubjectHouse houseSubject = (SubjectHouse) Enhancer.create(target.getClass(), new CGLibMethodInterceptor(target));houseSubject.rentHouse();

上述存在一个比较重要的知识点:

JDK动态代理存在一个致命的问题,即只能代理接口,不能代理类,而CGLIB既可以代理接口,又可以代理类;

🚀2.2Spring AOP原理总结

 spring AOP原理,是要从源码进行解读的,但是源码过于复杂,最终的情况就是如下所示:

在源码中的代理⼯⼚有⼀个重要的属性: proxyTargetClass, 默认值为false. 也可以通过程序设置(这里的默认值是根据不同情况来进行定义的)

大致意思就是在 proxyTargetClass为false时,在实现接口时使用JDK代理,只实现类的情况下,使用CGLIB代理,若proxyTargetClass,为true,那么所有实现方式都使用CGLIB代理;

Spring默认使用的的proxyTargetClass为false,但是在spring boot2.x之后proxyTargetClass默认为true,使用CGLIB进行代理;

当然我们可以使用配置项进行配置

spring.aop.proxy-target-class=false

 这里设置proxyTargetClass默认为false;

📚️3.总结

本期主要讲解了关于切点表达式的两种表达方式,以及Spring AOP实现原理的两种代理模式,即JDK代理,以及CGLIB代理,最后进行Spring AOP原理的总结;

🌅🌅🌅~~~~最后希望与诸君共勉,共同进步!!!


💪💪💪以上就是本期内容了, 感兴趣的话,就关注小编吧。

       😊😊  期待你的关注~~~

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

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

相关文章

Objective-C语言的软件工程

Objective-C语言的软件工程探讨 引言 在软件工程的领域中&#xff0c;编程语言的选择是至关重要的。Objective-C&#xff0c;作为一种为苹果公司的macOS和iOS操作系统而开发的编程语言&#xff0c;凭借其灵活性和强大的功能被广泛应用于应用开发。然而&#xff0c;随着Swift等…

GO通过SMTP协议发送邮件

什么是SMTP协议 SMTP&#xff08;Simple Mail Transfer Protocol&#xff0c;简单邮件传输协议&#xff09;是用于发送邮件的协议。当一个邮件服务器需要发送邮件给另一个邮件服务器时&#xff0c;它会使用SMTP协议与目标服务器建立连接&#xff0c;并传输邮件内容。SMTP协议的…

windows下,golang+vscode+delve 远程调试

1 先在远程服务器安装golang和delve golang的安装&#xff0c;通过官网直接下载安装包安装接口 go install github.com/go-delve/delve/cmd/dlvlatest 如果dlv和golang版本不匹配&#xff0c;这里把latest换成匹配的版本&#xff0c;比如1.20.0 2 编译带调试信息的程序 go bu…

Linux之线程池与单例模式

目录 线程池 线程池代码 单例模式 饿汉模式单例模式 懒汉模式单例模式 在前几期&#xff0c;我们已经学习了多线程的创建和控制&#xff0c;学习了多线程中的同步和互斥&#xff0c;学习了多线程中的条件变量和信号量&#xff0c;基于此我们实现了基于阻塞队列和基于环形队…

青蛙云的云服务器有哪些显著优势?

青蛙云的云服务器具有以下显著优势&#xff0c;这些特点使得它成为企业构建高效IT基础设施的重要选择&#xff1a; 1.高性能硬件 青蛙云服务器采用高性能的服务器硬件进行部署&#xff0c;确保服务的稳定性和高效运行&#xff0c;为用户提供卓越的计算能力。 2. 弹性资源 支持资…

赛车微型配件订销管理系统(源码+lw+部署文档+讲解),源码可白嫖!

摘要 赛车微型配件行业通常具有产品多样性、需求不确定性、市场竞争激烈等特点。配件供应商需要根据市场需求及时调整产品结构和库存&#xff0c;同时要把握好供应链管理和销售渠道。传统的赛车微型配件订销管理往往依赖于人工经验和简单的数据分析&#xff0c;效率低下且容易…

原生js解析token信息

原生js解析token信息&#xff1a; 代码&#xff1a; const tokenString localStorage.getItem(Token) const strings tokenString.split(.) //通过split()方法将token转为字符串数组 //取strings[1]数组中的第二个字符进行解析 const userinfo JSON.parse(decodeURICompon…

KGA:AGeneral Machine Unlearning Framework Based on Knowledge Gap Alignment

文章目录 摘要1 引言2 相关工作3 符号与定义4 我们的 KGA 框架4.1 KGA框架知识差距对齐目标 4.2 KGA在自然语言处理任务中的应用文本分类机器翻译响应生成 5 实验设置数据集评估指标参数设置比较方法 6 实验结果6.1 主要比较结果6.2 KGA 的优越性分析降低语言模型概率比较 6.3 …

swiftUI桥接UIKit

swiftUI桥接UIKit struct TestView: UIViewRepresentable {Binding var text: String // 该文本在swiftUI中可以修改let text: String // 不可修改func makeUIView(context: Context) -> UIView {let view UIView()// 例子asd.frame CGRect(x: 0, y: 0, width: 300, heig…

解决安装dlib失败

安装dlib时失败&#xff0c;报错如下&#xff1a; $pip install dlib Defaulting to user installation because normal site-packages is not writeable Looking in indexes: https://mirrors.huaweicloud.com/repository/pypi/simple Collecting dlibDownloading https://mi…

《上古重生》V20241127111039官方中文学习版

《上古重生》官方中文版https://pan.xunlei.com/s/VODab-jcgXUAgJbj2b1-11qUA1?pwdc7di# 一款考验玩家技巧的砍杀游戏&#xff0c;从玩家到敌人&#xff0c;通通身穿古代的厚重盔甲&#xff0c;进行“重量级”的近身搏斗。在充满Cult元素的第一人称视角大冒险中杀出一条血路吧…

什么是负载均衡?NGINX是如何实现负载均衡的?

大家好&#xff0c;我是锋哥。今天分享关于【什么是负载均衡&#xff1f;NGINX是如何实现负载均衡的&#xff1f;】面试题。希望对大家有帮助&#xff1b; 什么是负载均衡&#xff1f;NGINX是如何实现负载均衡的&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源…

数组排序------冒泡排序

要求&#xff1a;给定一个数组&#xff0c;让数组升序&#xff08;降序&#xff09;排序 思路&#xff1a; 假设升序排序&#xff1a; ①&#xff1a;将数组中相邻元素从前往后依次进行比较&#xff0c;如果前一个元素比后一个元素大&#xf…

Qt 坐标系统和坐标变换

一、概述:1、QPainter在QPaintDevice上绘图的默认坐标系统是&#xff0c;原点(0,0)在左上角&#xff0c;x轴正方向水平向右&#xff0c;y轴正方向竖直向下的坐标系。 2、为了绘图的方便&#xff0c;QPainter提供了一些坐标变换的功能&#xff0c;通过平移、旋转、缩放等坐标变…

SpringBoot + 九天大模型(文生图接口)

目录 1、先到九天大模型的官网&#xff08;LLM Studio&#xff09;上订阅模型的推理服务&#xff0c;得到APIKey&#xff0c;后期需要使用它生成token才能调用模型的推理服务。 2、在SpringBoot项目里面的pom.xml文件中添加九天大模型的相关依赖&#xff0c;后面会使用到其中…

1. npm 常用命令详解

npm 常用命令详解 npm&#xff08;Node Package Manager&#xff09;是 Node.js 的包管理工具&#xff0c;用于安装和管理 Node.js 应用中的依赖库。下面是 npm 的一些常用命令及其详细解释和示例代码。 镜像源 # 查询当前使用的镜像源 npm get registry# 设置为淘宝镜像源 …

12. C语言 数组与指针(深入理解)

本章目录: 前言1. 什么是数组&#xff1f;2. 数组的声明与初始化声明数组初始化数组 3. 访问数组元素遍历数组 4. 获取数组长度使用 sizeof 获取长度使用宏定义简化 5. 数组与指针数组名与指针的区别使用指针操作数组 6. 多维数组遍历多维数组 7. 数组作为函数参数8. 高级技巧与…

Leetcode 120. 三角形最小路径和 动态规划

原题链接&#xff1a;Leetcode 120. 三角形最小路径和 class Solution { public:int minimumTotal(vector<vector<int>>& triangle) {int n triangle.size();if (n 1)return triangle[0][0];int dp[n][n];dp[0][0] triangle[0][0];int res INT_MAX;for (in…

网络安全概论

网络安全概论--网络基础知识--防火墙技术--身份识别技术--虚拟专用网络--入侵检测技术--病毒和恶意代码 --业务连续性计划--安全管理--信息系统安全方案设计方法 一、网络安全面临的威胁 物理安全威胁、操作系统的安全缺陷、网络协议的安全缺陷、应用软件的实现缺陷、用户使…

设计模式 行为型 状态模式(State Pattern)与 常见技术框架应用 解析

状态模式&#xff08;State Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许对象在内部状态改变时改变其行为&#xff0c;使得对象看起来好像修改了它的类。这种设计模式的核心思想是将对象的状态和行为封装成不同的状态类&#xff0c;通过状态对象的行为改变来避免…