Spring高手之路-Spring AOP

目录

什么是AOP

Spring AOP有如下概念

补充:

AOP是如何实现的

Spring AOP 是通过代理模式实现的。

Spring AOP默认使用标准的JDK动态代理进行AOP代理。


什么是AOP

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

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

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

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

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

Spring AOP有如下概念

对于通知类型来说:

补充:

AOP是如何实现的

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

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {//...//检查AwareinvokeAwareMethods(beanName, bean);//调用BeanPostProcessor的前置处理方法Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);}//调用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, beanName);}return wrappedBean;
}

applyBeanPostProcessorsAfterInitialization中会遍历所有BeanPostProcessor,然后

调用其postProcessAfterInitialization方法,而AOP代理对象的创建就是AbstractAutoProxyCreator这个类的postProcessAfterInitialization方法中

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

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

/*** 如果需要,对bean进行包装。** @param bean 要包装的目标对象* @param beanName bean的名称* @param cacheKey 缓存键* @return 包装后的对象,可能是原始对象或代理对象*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {// 如果beanName不为null且在目标源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创建代理对象Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);if (specificInterceptors != DO_NOT_PROXY) {// 将缓存键对应的值设置为Boolean.TRUEthis.advisedBeans.put(cacheKey, Boolean.TRUE);// 创建代理对象Object proxy = createProxy(bean.getClass(), beanName, 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, beanName, beanClass);}// 创建ProxyFactory实例,并从当前代理创建器复制配置ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);// 如果不强制使用CGLIB代理目标类,根据条件决定是否使用CGLIB代理if (!proxyFactory.isProxyTargetClass()) {if (shouldProxyTargetClass(beanClass, beanName)) {proxyFactory.setProxyTargetClass(true);} else {// 根据bean类评估代理接口evaluateProxyInterfaces(beanClass, proxyFactory);}}// 构建advisor数组Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);// 将advisors添加到ProxyFactory中proxyFactory.addAdvisors(advisors);// 设置目标源proxyFactory.setTargetSource(targetSource);// 定制ProxyFactorycustomizeProxyFactory(proxyFactory);// 设置代理是否冻结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动态代理有一个缺点,就是它不能代理没有接口的类。

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

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

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

相关文章

开发人工智能 需要什么工具

人工智能&#xff08;Artificial Intelligence, AI&#xff09;是指利用计算机模拟、扩展和延伸人类智能的理论、方法、技术和应用系统的一门学科。人工智能研究的目标是使计算机能够像人类一样具有智能&#xff0c;能够感知和理解环境、学习和推理、决策和规划&#xff0c;具备…

jar 运行清单文件MANIFEST.MF生成定义Main-Class Premain-Class IDEA maven-assembly-plugin

可运行jar文件中的启动清单文件 META-INF/MANIFEST.MF 内容自定义生成 清单文件中的 Main-Class: Premain-Class: Can-Retransform-Classes: 在maven-assembly-plugin插件中的生成配置如下, 注意命名 <archive> <manifest> <mainClass>c…

MySQL中的like模糊查询

like与通配符搭配使用进行模糊查询 通配符 % 表示任意多个字符 _ 表示任意单个字符 #用户名中包含a的 select * from user where name like %a% #用户名中第二个字符为a的 select * from user where name like _a% #用户名中第二个字符为_的(需要使用转义字符) select * from u…

强烈推荐,提高开发效率的IDEA插件

自动生成JVM诊断工具arthas的命令 arthas idea (2.36) 基于IntelliJ IDEA开发的Alibaba Arthas 命令生成插件&#xff0c;支持Alibaba Arthas 官方常用的命令。 官方的命令太复杂&#xff0c;此插件为了简化使用者的难度&#xff0c;提供了一些常用命令的支持 该插件支持Aliba…

Dockerfile构建镜像

Dockerfile构建镜像 Dockerfile 是一个文本格式的配置文件&#xff0c; 用户可以使用 Dockerfile 来快速创建自定义的镜像&#xff0c;另外&#xff0c;使 用Dockerfile去构建镜像好比使用pom去构建maven项目一样&#xff0c;有异曲同工之妙 基本结构 Dockerfile 由一行行…

Intel® SGX Instruction References(五)

文章目录 前言一、Intel SGX Instruction Syntax and Operation1.1 ENCLS Register Usage Summary1.2 ENCLU Register Usage Summary1.3 ENCLV Register Usage Summary1.4 Information and Error Codes1.5 Internal CREGs1.6 Concurrent Operation Restrictions 二、Intel SGX …

Nodejs 使用 fs-extra 模块进行目录和文件操作

在Node.js中&#xff0c;fs-extra模块是fs模块的扩展&#xff0c;提供了一些额外的功能&#xff0c;使文件和目录的操作更加便捷。那么我们如何使用 fs-extra 模块进行目录和文件操作呢&#xff1f;以下是一些常见的用法示例&#xff1a; 1. ensureDir/ensureDirSync - 创建目…

WuBit:聚合BRC20资产交易,续写铭文市场新浪潮

2023年的数字货币领域见证了重大的创新&#xff0c;尤其是比特币Ordinals协议的推出&#xff0c;它为铭文赛道带来了新的生命力。这一协议的核心在于将比特币分割为更小的单位——聪&#xff0c;并在每个聪上嵌入独特信息&#xff0c;从而创建了一种新颖的非同质化资产。BRC20等…

亚马逊云科技 re:Invent 2023 产品体验:亚马逊云科技产品应用实践 王炸产品 Amazon Q,你的 AI 助手

意料之中 2023年9月25日&#xff0c;亚马逊宣布与 Anthropic 正式展开战略合作&#xff0c;结合双方在更安全的生成式 AI 领域的先进技术和专业知识&#xff0c;加速 Anthropic 未来基础模型的开发&#xff0c;并将其广泛提供给亚马逊云科技的客户使用。 亚马逊云科技开发者社…

TypeScript学习(基础篇)

前言 在现代的Web开发生态系统中&#xff0c;JavaScript已经成为一种必备的技术。然而&#xff0c;随着应用的增大&#xff0c;JavaScript的一些限制开始显现&#xff0c;例如缺乏静态类型检查和编译时错误检查。这正是TypeScript发挥作用的地方&#xff0c;TypeScript是一种静…

GO语言基础笔记(四):并发编程基础

目录 Goroutines 通道&#xff08;Channel&#xff09; 代码示例 Goroutines 定义与特点&#xff1a; Goroutines是Go语言中实现并发的基本单位。它比传统的线程更轻量级&#xff0c;拥有更小的内存占用和更快的启动时间。在Go程序中&#xff0c;您可以轻松地启动成千上万的G…

Redis基本介绍和使用场景

1、什么是Redis Redis 是一种基于内存的数据库&#xff0c;对数据的读写操作都是在内存中完成&#xff0c;因此读写速度非常快&#xff0c;常用于缓存&#xff0c;消息队列、分布式锁等场景。 Redis 提供了多种数据类型来支持不同的业务场景&#xff0c;比如 String(字符串)、…

【Linux系统基础】(2)在Linux上部署MySQL、RabbitMQ、ElasticSearch等各类软件

实战章节&#xff1a;在Linux上部署各类软件 前言 为什么学习各类软件在Linux上的部署 在前面&#xff0c;我们学习了许多的Linux命令和高级技巧&#xff0c;这些知识点比较零散&#xff0c;同学们跟随着课程的内容进行练习虽然可以基础掌握这些命令和技巧的使用&#xff0c;…

vue跑马灯

vue跑马灯 组件propscolOption 使用elementui的布局组件实现列宽度 <template><div class"lamp-container"><div class"lamp-header"><el-row :gutter"10"><el-col v-for"col in column" :key"col.pr…

12.26ARM作业

三个按键中断&#xff0c;控制对应灯亮灭 main.c #include "key_it.h"void delay(int ms){int i,j;for(i0;i<ms;i){for(j0;j<2000;j);}}int main(){all_led_init();key1_it_config();key2_it_config();key3_it_config();while(1){printf("do main...\n&…

Linux c++开发-14-IO复用

什么是文件 程序员使用I/O最终都逃不过文件这个概念。 在Linux世界中文件是一个很简单的概念&#xff0c;作为程序员我们只需要将其理解为一个N byte的序列就可以了&#xff1a; b1, b2, b3, b4, … bN 实际上所有的I/O设备都被抽象为了文件这个概念&#xff0c;一切皆文件…

常用命令-设置

目录 系统配置查看系统架构屏幕演示工具合并终端命令Windows cmd命令提示符重启网卡禁止系统更新CMD运行powshell获取文件安装目录微软VC运行库合集强制刷新IP默认程序打开文件SSH免密登录关闭IE增强配置警告滑动关机最近操作记录解决谷歌翻译禁止系统休眠文件传输文件批量改名…

基于Java (spring-boot)的宠物管理系统

一、项目介绍 1、用户端功能&#xff1a; 首页&#xff1a;展示公告列表&#xff0c;宠物科普&#xff0c;介绍流浪宠物&#xff0c;热门活动。 宠物领养&#xff1a;用户搜索想要领养宠物&#xff0c;申请领养&#xff0c;查看自己领养的宠物。 宠物救助&#xff1a;用户能…

C# .Net学习笔记—— 加密和解密算法

一、四种加密方式 1、MD5不可逆加密 2、Des对称可逆加密 3、RSA非对称可逆加密 4、数字证书SSL 二、详解 1、MD5加密 public class MD5Encrypt{public static string Encrypt(string source, int length 32){if (string.IsNullOrEmpty(source)) return string.Empty;HashA…

002、使用 Cargo 创建新项目,打印 Hello World

1. Cargo 简介 Cargo 是 Rust 的构建系统和包管理工具&#xff0c;比如构建代码、下载依赖的库、构建这些库等等。在安装 Rust 时&#xff0c;Cargo也会一起安装。 2. 创建新项目的具体步骤 步骤1&#xff1a; 我们在桌面新建一个文件夹&#xff0c;用于存放后面练习用的代码文…