【23.12.30期--Spring篇】Spring的AOP介绍(详解)

在这里插入图片描述

Spring的AOP介绍

  • ✔️简述
  • ✔️扩展知识
    • ✔️AOP是如何实现的


✔️简述


AOP(Aspect-Oriented Programming),即面向切面编程,用人话说就是把公共的逻辑抽出来,让开发者可以更专注于业务逻辑开发。


和IOC-样,AOP也指的是一种思想。AOP思想是OOP (Obiect-Oriented Programming) 的补充OOP是面向类和对象的,但是AOP则是面向不同切面的。一个切面可以横跨多个类和对象去操作,极大的丰富了开发者的使用方式,提高了开发效率。


譬如,一个订单的创建,可能需要以下步骤:


1.权限校验
2.事务管理
3.创建订单
4.日志打印


如果使用AOP思想,我们就可以把这四步当成四个“切面”,让业务人员专注开发第三个切面,其他二个切面则是基础的通用逻辑,统一交给AOP封装和管理。


Spring AOP有如下概念(列举下,不用刻意记):


术语翻译释义
Aspect切面切面由切入点和通知组成,它既包含了横切逻辑的定义,也包括了切入点的定义。切面是一个横切关注点的模块化,一个切面能够包含同一个类型的不同增强方法,比如如说事务处理和日志处理可以理解为两人切面。
PointCut切入点切入点是对连接点进行拦截的条件定义,决定通知应该作用于截哪些方法。 (充当where角色,即在哪里做)
Advice通知通知定义了通过切入点拦截后,应该在连接点做什么,是切面的具体行为。 (充当what角色,即做什么)
Target目标对象目标对象指将要被增强的对象,即包含主业务逻辑的类对象。或者说是被一个或者多个切面所通知的对象。
JoinPoint连接点连接点是程序在运行时的执行点,这个点可以是正在执行的方法,或者是正在抛出的异常。因为Spring只支持方法类型的连接点,所以在Spring中连接点就是运行时刻被拦截到的方法。连接点由两个信息确定: 1 . 方法(表示程序执行点,即在哪个目标方法) 2 . 相对点(表示方位,即目标方法的什么位置,比如调用前,后等)
Weaving织入织入是将切面和业务逻辑对象连接起来,并创建通知代理的过程。织入可以在编译时,类加载时和运行时完成。在编译时进行织入就是静态代理,而在运行时进行织入则是动态代理。

对于通知类型来说:


以下是一个2列5行的表格:

术语释义
Before Advice连接点执行前执行的逻辑
After returning advice连接点正常执行(未抛出异常) 后执行的逻辑
After throwing advice连接点抛出异常后执行的逻辑
After finally advice无论连接点是正常执行还是抛出异常,在连接点执行完毕后执行的逻辑
Around advice该通知可以非常灵活的在方法调用前后执行特定的逻辑

✔️扩展知识


✔️AOP是如何实现的


SpringBean的初始化流程下一篇会单独做详细说明。


从Bean的初始化流程中来讲,Spring的AOP会在bean实例的实例化已完成,进行初始化后置处理时创建代理对象,即下面代码中的applyBeanPostProcessorsAfterlnitialization部分。


protected Object initializeBean(final String beanlame, final Object bean, RootBeanDefinition mbd) {//....//检查AwareinvokeAwareMethods(beanName , bean);//调用BeanPostProcessor的前置处理方法Object wrappedBean = bean;if (mbd == null  || !mbd.isSynthetic())  {wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanlame);}//调用InitializingBean的afterPropertiesSet方法或自定义的初始化方法及自定义init-method方法try {invokeInitMethods(beanName,wrappedBean, mbd);} catch (Throwable ex) {throw new BeanCreationException((mbd != null ? mbd.getResourceDescription() : null),beanName,"Invocation of init method failed", ex);}//调用BeanPostProcessor的后置处理方法if (mbd == null || !mbd.issynthetic()) {wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanlame);}return wrappedBean;
}

applyBeanPostProcessorsAfterlnitialization中会遍历所有BeanPostProcessor,然后调用其postProcessAfterlnitialization方法,而AOP代理对象的创建就是在AbstractAutoProxyCreator这个类的postProcessAfterlnitialization口中:


@override
public Object postProcessAfterInitialization(0bject bean, String beanlame) throws BeansException {if ((bean != null) {Object cacheKey = getCacheKey(bean.getClass(),beanName);if (this.earlyProxyReferences.remove(cacheKey) != bean) {if (this.earlyProxyReferences.remove(cacheKey) != bean) ;}}return bean;
}

这里面最重要的就是wrapIfNecessary方法了:


/**
*    如果需要,对bean进行包装。
*    
* 	@param bean 要包装的目标对象
* 	@param beanName bean的名称
* 	@param cacheKey 缓存键
* 	 @return 包装后的对象,可能是原始对象或代理对象
*/
protected Object wrapIfNecessary(Object bean, String beanlame, Object cachekey) {//如果beanName不为nul1且在目标源bean集合中,则直接返回原始对象if (beanName != null && this.targetSourcedBeans.contains(beanName)) {return bean;}//如果缓存键对应的值为 Boolean.FALSE,则直接返回原始对象if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}// 如果bean的类型为基础设施类,或者应跳过该类型的代理,则将缓存键对应的值设置为Boolean.FALSE并返回原始对象if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey Boolean.FALSE);return bean;}//如果存在advice,为bean创建代理对象Objectl] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanlame, null);if (specificInterceptors != DO NOT PROXY) {//将缓存键对应的值设置为 Boolean.TRUEthis .advisedBeans.put(cacheKey, Boolean.TRUE);//创建代理对象Object proxy = createProxy(bean.getClass(), beanlame, specificInterceptors, new SingletonTargetSource(bean));//将代理对象的类型与缓存键关联起来this .proxyTypes.put(cacheKey,proxy.getClass());return proxy;}// 如果没有advice,将缓存键对应的值设置为Boolean.FALSE并返回原始对象this.advisedBeans.put(cacheKey,Boolean.FALSE);return bean;
}

createProxy的主要作用是根据给定的bean类,bean名称、特订拦截器和目标源,创建代理对象:


/**
*    根据给定的bean类、bean名称、特定拦截器和目标源,创建代理对象。
*     
*     @param beanClass 要代理的目标对象的类
*     @param beanName bean的名称
*     @param specificInterceptors   特定的拦截器数组
*     @param targetSource   目标源
*     @return 创建的代理对象
*/
protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource)  {//如果beanFactory是ConfigurableListableBeanFactory的实例,将目标类暴露给它if (this.beanFactory instanceof ConfigurableListableBeanFactory) {AutoProxyUtils.exposeTargetClass((ConfigurablelistableBeanFactory) this.beanFactory, beanllame, beanClass);}// 创建ProxyFactory实例,并从当前代理创建器复制配置ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);//如果不强制使用CGLIB代理目标类,根据条件决定是否使用CGLIB代理if (!proxyFactory.isProxyTargetClass())  {if (!proxyFactory.isProxyTargetClass()) {proxyFactory.setProxyTargetClass(true);} else {//根据bean类评估代理接口evaluateProxyInterfaces(beanClass,proxyFactory);}}// 构建advisor数组Advisor[] advisors = buildAdvisors(beaname, specificInterceptors);//将advisors添加到ProxyFactory中proxyFactory.addAdvisors(advisors);//设置目标源proxyFactory.setTargetSource(targetSource);//设置目标源proxyFactory.setTargetSource(targetSource);// 设置代理是否冻结proxyFactory.setFrozen(this.freezeProxy);//如果advisors已经预过滤,则设置ProxyFactory为预过滤状态if (advisorsPreFiltered())  {proxyFactory.setPreFiltered(true);}//获取代理对象,并使用指定的类加载器return proxyFactory.getProxy(getProxyClassLoader());}

Spring AOP 是通过代理模式实现的,具体有两种实现方式,一种是基于Java原生的动态代理种是基于cglib的动态代理。对应到代码中就是,这里面的Proxy有两种实现,分别是CglibAopProxy和JdkDynamicAopProxy。


Spring AOP默认使用标准的JDK动态代理进行AOP代理。这使得任何接口可以被代理。但是JDK动态代理有一个缺点,就是它不能代理没有接口的类。




所以SpringAOP就使用CGLIB代理没有接口的类。


在这里插入图片描述

当然代理这种设计模式也有动态代理和静态代理之分


参考链接: Java动态代理如何实现

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

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

相关文章

字符串函数的实现以及大小写转换、字符统计等------(C每日一编程)

--基本算法&#xff1a; --字符串求长度、拷贝、比较、连接 --大小写转换、字符统计 -其他&#xff1a; --判断回文、逆序、删字符、字符定位 --输入&#xff08;gets&#xff09;、输出&#xff08;puts&#xff09; 正文解读&#xff1a; 大小写转换&#xff1a;就是加减32…

Shell三剑客:awk(awk编辑编程)六

一、AWK 的函数前言 awk的函数有许多&#xff0c;除了系统自带的内建函数还有就是用户自定义的函数。 二、AWK 常用的函数 rand() # 返回0 和1 之间一个随机数srand() # 生成随机数种子int() # 取整数length([s]) # 返回指定字符串的长度sub(r,s,[t]) # 对t字符串进行搜索&am…

【C++杂货铺】C++11新特性——lambda

文章目录 一、C98中的排序二、先来看看 lambda 表达式长什么样三、lambda表达式语法3.1 捕捉列表的使用细节 四、lambda 的底层原理五、结语 一、C98中的排序 在 C98 中&#xff0c;如果要对一个数据集合中的元素进行排序&#xff0c;可以使用 std::sort 方法&#xff0c;下面…

Spring Boot 基于Redisson实现注解式分布式锁

依赖版本 JDK 17 Spring Boot 3.2.0 Redisson 3.25.0 源码地址&#xff1a;Gitee 导入依赖 <properties><redisson.version>3.25.0</redisson.version> </properties><dependencies><dependency><groupId>org.projectlombok</…

为什么网络安全行业那么缺人才,但招聘岗位却没那么多?

文章目录 一、学校的偏向于学术二、学的东西太基础三、不上班行不行 为什么网络安全的人才缺口那么大&#xff0c;但是大学毕业能找到网安工作的人却很少&#xff0c;就连招聘都没有其他岗位多&#xff1f; 明明央视都说了网络安全的人才缺口还有300多万&#xff0c;现在找不…

Python 下载与安装

1、下载 打开Python官网&#xff1a;Welcome to Python.org 点击下图所示的【Downloads】按钮进入下载页面。 ​ 进入下载页面后下拉至下图位置&#xff0c;选择版本&#xff0c;点击下载按钮下载。 页面会跳转至下一页下载页面&#xff0c;下拉到下图位置&#xff0c;选择…

18 UVM Scoreboard

UVM scoreboard是一个检查DUT功能的组件。它用analysis export从monitor接受transaction事务以进行检查。 uvm_scoreboard class declaration: virtual class uvm_scoreboard extends uvm_component User-defined scoreboard class declaration: 用户定义的scoreboard是从 u…

非对称加密与对称加密的区别是什么?

在数据通信中&#xff0c;加密技术是防止数据被未授权的人访问的关键措施之一。而对称加密和非对称加密是两种最常见的加密技术&#xff0c;它们被广泛应用于数据安全领域&#xff0c;并且可以组合起来以达到更好的加密效果。本文将探讨这两种技术的区别&#xff0c;以及它们在…

OpenHarmony 快速集成轻量级存储(dataPreferences)工具类

OpenHarmony中也有类似于Android的SharedPreferences的轻量级存储&#xff0c;名字叫dataPreferences&#xff0c;这里省略介绍&#xff0c;直接放工具类 一.初始化 首先在EntryAbility中对dataPreferences进行初始化操作&#xff0c;初始化的代码如下&#xff1a; import d…

基于Java+SpringMvc+Vue求职招聘系统详细设计实现

基于JavaSpringMvcVue求职招聘系统详细设计实现 &#x1f345; 作者主页 专业程序开发 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; 文章目录 基于JavaSpringMvcVue求职招聘系统详细设计实现一、前言介…

Nacos注册及使用

一、简介 Nacos是阿里云开源的一个服务发现、配置管理和服务鉴权平台&#xff0c;它提供了一种更简单、更便捷、更开放的方式来管理服务&#xff0c;帮助开发者快速实现服务的发现、配置的管理、服务的鉴权等功能。Nacos可以帮助开发者轻松管理微服务应用中的服务提供者、服务…

啊?这也算事务?!

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 学习必须往深处挖&…

GBASE南大通用-GBase 8s数据库日志模式及切换

一、 GBase 8s数据库共有以下 4 种日志模式&#xff1a;无日志模式、缓冲日志模式、无缓冲日志模式、ANSI 模式。详细介绍如下&#xff1a; 1、无日志模式&#xff08;Non logging&#xff09;&#xff1a; 采用无日志模式时&#xff0c;所有 DML 操作都不会被记录到日志中&…

关于表格太大了jupyter无法单次处理的问题

记录下自己的心路历程…耗时耗精力 我用的数据库单个表格就很大&#xff0c;一个表格有30多G&#xff0c;jupyter无法处理这么大的表格&#xff0c;会直接把电脑的进程全部结束掉&#xff0c;结束掉要是能运行成功倒也行啊&#xff0c;然鹅…给我报错说处理不了&#xff0c;罢工…

代码随想录刷题 | Day1

今日学习目标 一、基础 数组 array类 模板类vector 数组是存放在连续内存空间上的相同类型数据的集合。 数组可以方便的通过下标索引的方式获取到下标下对应的数据。 需要两点注意的是 数组下标都是从0开始的。 数组内存空间的地址是连续的 而且大家如果使用C的话&…

儿童学python语言能做什么,儿童学python哪个机构好

这篇文章主要介绍了儿童学python哪个线上机构好&#xff0c;具有一定借鉴价值&#xff0c;需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获&#xff0c;下面让小编带着大家一起了解一下。 少儿编程python 文章目录 前言 CSP-J与CSP-S少儿编程证书含金量排名&#xff0…

linux实用技巧:ubuntu18.04安装samba服务器实现局域网文件共享

Ubuntu安装配置Samba服务与Win10共享文件 Chapter1 Ubuntu18.04安装配置Samba服务与Win10共享文件一、什么是Samba二、安装Samba1、查看是否有安装samba2、安装samba 三、配置Samba服务1、创建共享目录&#xff08;以samba_workspaces为例&#xff09;2、为samba设置登录用户3、…

Python装饰器的专业解释

装饰器&#xff0c;其实是用到了闭包的原理来进行操作的。 单个装饰器&#xff1a; 以下是一个简单的例子&#xff1a; def outer(func):print("OUTER enter ...")def wrapper(*args, **kwargs):print("调用之前......")result func(*args, **kwargs)p…

亚马逊云科技 re:Invent 2023 产品体验:亚马逊云科技产品应用实践 国赛选手带你看 Elasticache Serverless

抛砖引玉 讲一下作者背景&#xff0c;曾经参加过国内世界技能大赛云计算的选拔&#xff0c;那么在竞赛中包含两类&#xff0c;一类是架构类竞赛&#xff0c;另一类就是 TroubleShooting 竞赛&#xff0c;对应的分别为亚马逊云科技 GameDay 和亚马逊云科技 Jam&#xff0c;想必…

【数学建模美赛M奖速成系列】Matplotlib绘图技巧(三)

Matplotlib绘图技巧&#xff08;三&#xff09; 写在前面7. 雷达图7.1 圆形雷达图7.2 多边形雷达图 8. 极坐标图 subplot9. 折线图 plot10. 灰度图 meshgrid11. 热力图11.1 自定义colormap 12. 箱线图 boxplot 写在前面 终于更新完Matplotlib绘图技巧的全部内容&#xff0c;有…