介绍一下Spring的AOP

一、问题解析

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

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

譬如,一个订单的创建,可能需要以下步骤:
1权限校验
2事务管理
3创建订单
4日志打印
如果使用AOP思想,我们就可以把这四步当成四个“切面”,让业务人员专注开发第三个切面,其他三个切面则是基础的通用逻辑,统一交给AOP封装和管理。

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

术语

翻译

释义

Aspect

切面

切面由切入点和通知组成,它既包含了横切逻辑的定义,也包括了切入点的定义。切面是一个横切关注点的模块化,一个切面能够包含同一个类型的不同增强方法,比如说事务处理和日志处理可以理解为两个切面。

PointCut

切入点

切入点是对连接点进行拦截的条件定义,决定通知应该作用于截哪些方法。(充当where角色,即在哪里做)

Advice

通知

通知定义了通过切入点拦截后,应该在连接点做什么,是切面的具体行为。(充当what角色,即做什么)

Target

目标对象

目标对象指将要被增强的对象,即包含主业务逻辑的类对象。或者说是被一个或者多个切面所通知的对象。

JoinPoint

连接点

连接点是程序在运行时的执行点,这个点可以是正在执行的方法,或者是正在抛出的异常。因为Spring只支持方法类型的连接点,所以在Spring中连接点就是运行时刻被拦截到的方法。连接点由两个信息确定:
●方法(表示程序执行点,即在哪个目标方法)
●相对点(表示方位,即目标方法的什么位置,比如调用前,后等)

Weaving

织入

织入是将切面和业务逻辑对象连接起来, 并创建通知代理的过程。织入可以在编译时,类加载时和运行时完成。在编译时进行织入就是静态代理,而在运行时进行织入则是动态代理。

对于通知类型来说:

Before Advice

连接点执行前执行的逻辑

After returning advice

连接点正常执行(未抛出异常)后执行的逻辑

After throwing advice

连接点抛出异常后执行的逻辑

After finally advice

无论连接点是正常执行还是抛出异常,在连接点执行完毕后执行的逻辑

Around advice

该通知可以非常灵活的在方法调用前后执行特定的逻辑

扩展知识

AOP是如何实现的? 

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

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {

    //...
    //检查Aware
    invokeAwareMethods(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.TRUE
        this.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);
    // 定制ProxyFactory
    customizeProxyFactory(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代理没有接口的类。

 二、粉丝福利

我是浮生,一个工作十四年经验的Java程序员!

最近很多同学问我有没有java学习资料,我根据我从小白到架构师多年的学习经验整理出来了一份80W字面试解析文档、简历模板、学习路线图、java必看学习书籍 、 需要的小伙伴 可以关注我
公众号:“ 
灰灰聊架构 ”, 回复暗号:“ 321 ”即可获取

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Java刷题篇】滑动窗口

文章目录 &#x1f4c3;滑动窗口&#x1f4dc;基本概念&#x1f4dc;核心思路 ✍最大连续1的个数 III✍水果成篮 &#x1f4c3;滑动窗口 &#x1f4dc;基本概念 滑动窗口是一种基于双指针的一种思想&#xff0c;两个指针指向的元素之间形成一个窗口。 分类&#xff1a;窗口有…

C++语言现在还有人学吗?

在当今信息爆炸的时代&#xff0c;计算机编程语言繁多&#xff0c;涌现了许多新兴的编程语言&#xff0c;如Python、JavaScript等。针对C编程语言是否还有人学的问题&#xff0c;我个人认为可以从以下几个方面进行讨论。 首先&#xff0c;C诞生于1979年&#xff0c;起初是为了开…

AI预测福彩3D第12弹【2024年3月18日预测--第3套算法重新开始计算第1次测试】

前面的第2套算法感觉效果比较差&#xff0c;与真实结果差距较大&#xff0c;因此&#xff0c;果断放弃第2套算法&#xff0c;再次进行了改进后&#xff0c;咱们从今天开始测试第3套算法。第3套算法加入了012路的权重。废话不多说了&#xff0c;直接上结果吧~ 最终&#xff0c;经…

数据驱动校园管理:山海鲸可视化软件看板搭建记

随着信息化时代的到来&#xff0c;校园管理也逐渐向数字化、可视化转型。作为一名数据分析师&#xff0c;我有幸参与了使用山海鲸可视化软件搭建校园管理可视化看板的项目&#xff0c;山海鲸可视化软件是近些年新崛起的一款可视化产品&#xff0c;支持免费可视化编辑、私有化部…

网络学习:IPV6地址详解

目录 前言&#xff1a; 一、IPV6的由来 二、什么是IPV6地址&#xff1f; IPV6地址结构&#xff1a; 前言&#xff1a; IPV6&#xff08;Internet Protocol Version 6&#xff09;是网络层协议的第二代标准协议&#xff0c;也被称为IPng&#xff08;IP Next Generation&…

SolidWorks教育版 科研版 商业版区别

SolidWorks是一款功能强大的三维CAD软件&#xff0c;广泛应用于机械设计、工业设计、建筑设计等领域。SolidWorks提供了多个版本&#xff0c;以满足不同用户的需求。本文将详细介绍SolidWorks教育版、科研版与商业版的区别&#xff0c;帮助你更好地选择适合自己的版本。 首先&…

sentinel熔断降级

熔断降级 Slot 责任链上的最后一环&#xff1a;熔断降级 DegradeSlot,熔断降级作为保护系统的一种强大手段,可以根据慢调用、异常比例和异常数进行熔断,并自定义持续时间以实现系统保护 规则配置 规则类中属性解析 与控制面板对应 // 其中资源名称在 AbstractRule 里。 pu…

计算机一级word 文字处理理论+实操试题

计算机一级word 文字处理理论实操试题 单选题&#xff1a; 1、在Word编辑状态下&#xff0c;要将另一文档的内容全部添加在当前文档的当前光标处&#xff0c;应选择的操作是依次单击______。 A.“文件”选项卡和“打开”项 B.“文件”选项卡和“新建”项 C.“插入”选项卡…

windows server 下的mysql 8.0.28修改数据库目录

1. 查看当前数据库存储位置 show global variables like %datadir%; 默认是&#xff1a;C:\ProgramData\MySQL\MySQL Server 8.0\Data 2. 修改 C:\ProgramData\MySQL\MySQL Server 8.0\my.ini配置文件。如下&#xff1a; datadirD:/ProgramData/MySQL/MySQL Server 8.0/Dat…

【HyperLips:】数字人——控制嘴唇 项目源码python实现

最近受到商汤“复活”汤晓鸥的视频刺激&#xff0c;大大的amazing&#xff01;没看过的小伙伴可以自行百度&#xff0c;看了不研究一下【数字人】技术&#xff0c;都要跟时代脱轨了&#xff0c;那就以HyperLips为开篇吧。 目录 &#x1f34e;&#x1f34e;1.摘要 &#x1f3…

OgGame——游戏全球发行的全套解决方案

在现今瞬息万变的游戏行业&#xff0c;成功发行一款游戏面临着各方面的难题&#xff0c;例如市场、版号、本土化等等。OgGame以其全球游戏发行的全套解决方案&#xff0c;成为开发者们的首选&#xff0c;为其提供了稳定而全面的支持。 为什么需要游戏全球发行解决方案&#xff…

专访沈劭劼:7千元干出城市NOA,大疆车载如何在「西瓜上雕树林」?

作者 |张祥威 编辑 |德新 在中国乃至全球智驾的供应商中&#xff0c;大疆车载是一家需要被重视的公司&#xff0c;这家公司在「极致性价比」的方向上进展极快。 去年&#xff0c;大疆发布了基于TITDA4 VH的量产方案&#xff0c;在五菱宝骏云朵等车型上进行了量产。一年后&…

C#,数值计算,数据测试用的对称正定矩阵(Symmetric Positive Definite Matrix)的随机生成算法与源代码

C.Hermite 1、对称矩阵 对称矩阵(Symmetric Matrices)是指以主对角线为对称轴,各元素对应相等的矩阵。在线性代数中,对称矩阵是一个方形矩阵,其转置矩阵和自身相等。1855年,埃米特(C.Hermite,1822-1901年)证明了别的数学家发现的一些矩阵类的特征根的特殊性质,如称为埃…

Spark杂谈

文章目录 什么是Spark对比HadoopSpark应用场景Spark数据处理流程什么是RDDSpark架构相关进程入门案例&#xff1a;统计单词数量Spark开启historyServer 什么是Spark Spark是一个用于大规模数据处理的统一计算引擎Spark一个重要的特性就是基于内存计算&#xff0c;从而它的速度…

Jmeter-实战案例(随机上传文件,接口依赖调用)

前置知识 1 两个接口 1-1 readData需要上传文件 参数 // formData类型 sdbh:"" file: "上传一个压缩包"响应 {"code": 1000,"status": "success","message": "操作成功","data":{"n…

es索引操作命令

索引操作 index 创建索引 put 方法创建索引 使用 put 创建索引时必须指明文档id&#xff0c;否则报错 # PUT 创建命令 # test1 索引名称 # type1 类型名称&#xff0c;默认为_doc&#xff0c;已经被废弃 # 1 文档id PUT /test1/type1/1 {"name":"zhangsan&…

第三门课:结构化机器学习项目-机器学习策略

文章目录 1 机器学习策略一1.1 为什么是ML策略&#xff1f;1.2 正交化1.3 单一数字评估指标1.4 满足和优化指标1.5 训练、开发及测试集划分1.6 开发集和测试集的大小1.7 什么时候改变开发、测试集和指标&#xff1f;1.8 为什么是人的表现&#xff1f;1.9 可避免偏差1.10 理解人…

贪心算法(算法竞赛、蓝桥杯)--线段覆盖

1、B站视频链接&#xff1a;A29 贪心算法 P1803 线段覆盖_哔哩哔哩_bilibili 题目链接&#xff1a;凌乱的yyy / 线段覆盖 - 洛谷 #include <bits/stdc.h> using namespace std;struct line{int l,r;bool operator<(line &b){return r<b.r;//重载小于号,按右端…

FreeRTOS的列表和列表项

这个章节的内容是非常重要的&#xff0c;因为 FreeRTOS 的源码实现离不开列表&#xff0c;所以说大家如果想要看懂 FreeRTOS 的源码&#xff0c;看它是如何实现的&#xff0c;那么这个列表你必须要掌握。 1. 列表和列表项 1.1 列表和列表项的简介 列表 是 FreeRTOS 中的一个…

【递归专题】【蓝桥杯备考训练】:有序分数、正则问题、带分数、约数之和、分形之城【已更新完成】

目录 1、有序分数&#xff08;usaco training 2.1&#xff09; 2、正则问题&#xff08;第八届蓝桥杯省赛C A组 & Java A组&#xff09; 3、带分数&#xff08;第四届蓝桥杯省赛Java A组/B组 & C B组/C组&#xff09; 4、约数之和&#xff08;《算法竞赛进阶指南》…