Spring AOP 基础知识

news2024/10/8 15:01:51
 

1.背景

按照软件重构的思想,当多个类中存在相同的代码时,需要提取公共部分来消除代码坏味道。Java的继承机制允许用户在纵向上通过提取公共方法或者公共部分(模版方法方式)至父类中以消除代码重复问题;日志、访问控制、性能监测等重复的非业务代码揉杂在业务代码之中无法横向抽取,AOP技术为其提供了一个解决方案。

AOP技术将这些重复的非业务代码抽取出为一个模块,通过技术整合还原代码的逻辑和功能;即:在代码层面上消除了重复度,提高了可维护性,并在功能层面上得到还原。抽取重复代码作为一个模块是用户的问题,然而技术整合(对目标织入增强逻辑,后文介绍)以实现功能还原是AOP的目标和工作重心,Spring AOP是该技术的一种实现。

 

本文作为Spring系列的第八篇,介绍Spring框架中AOP的使用、注意事项和实现原理,原理部分会结合Spring框架源码进行。

 

Spring系列的后续文章如Spring系列-9 Async注解使用与原理和Spring系列-10 事务机制其底层原理都是Spring AOP。

 

2.  AOP 简介

常见的AOP实现方案有Spring AOP和AspectJ:相对于Spring AOP而言,AspectJ是一种更成熟、专业的AOP实现方案。AOP的技术整合(织入增强逻辑)可以发生在编译器、类加载期以及运行期:AspectJ在编译器(ajc)和类加载器(使用特定的类加载器)实现;Spring AOP在运行时通过动态代理方式实现。AspectJ提供了完整了AOP方案,而Spring AOP从实用性出发未常见的应用场景提供了技术方案,如不支持静态方法、构造方法等的AOP。

 

Spring AOP构建于IOC之上,与IOC一起作为Spring框架的基石。Spring AOP底层使用动态代理技术实现,包括:JDK动态代理与CGLIB动态代理;JDK动态代理技术要求被代理对象基于接口,而CGLIB动态代理基于类的继承实现代理,从而要求被代理类不能为final类且被代理的方法不能被final、staic、private等修饰。二者都有局限性,在一定程度上相互弥补。

 

3.AOP基本概念

[1] 执行点:在Spring AOP中指代目标类中具体的方法;

[2] 连接点:包含位置信息的执行点,位置信息包括:方法执行前、后、前后、异常抛出等;

[3] 切点:根据指定条件(类是否符合、方法是否符合等)过滤出的执行点的集合;

[4] 通知/增强:为目标对象增加的新功能,如在业务代码中引入日志、访问控制等功能;

[5] 切面:切面由切点和通知组成;

[6] 织入:将切面织入目标对象,形成代理对象的过程。

 

4.增强类型

Spring中使用Advise标记接口表示增强,Spring根据方位信息(方法执行前后、环绕、异常抛出等)为其定义了不同的子类接口。

 

public interface Advice {}

 

 

4.1 前置增强

BeforeAdvice接口表示前置增强,由于Spring当前仅支持方法增强,所以可用的接口为MethodBeforeAdvice.

 

//同Advise接口,BeforeAdvice也是个空接口
public interface MethodBeforeAdvice extends BeforeAdvice {
	void before(Method method, Object[] args, @Nullable Object target) throws Throwable;
}

 

如上所示,MethodBeforeAdvice接口中仅有一个before方法,入参分别是方法对象、参数数组、目标对象;该方法会在目标对象的方法调用前调用。

4.2 后置增强

 

public interface AfterReturningAdvice extends AfterAdvice {
	void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;
}

 

该方法中仅有一个afterReturning方法,入参比before多处一个返回值;该方法会在目标对象的方法调用后调用。

 

4.3 环绕增强

 

@FunctionalInterface
// Interceptor 是Advise的字接口,且是空接口
public interface MethodInterceptor extends Interceptor {
	@Nullable
	Object invoke(@Nonnull MethodInvocation invocation) throws Throwable;
}

 

可通过invocation.proceed()语句调用目标对象方法并获得放回值,可在前后自定义逻辑,相对于前置和后置有更高的灵活性。

 

4.4 异常抛出增强

 

public interface ThrowsAdvice extends AfterAdvice {
}

 

ThrowsAdvice是一个空接口,起标签作用。在运行期间Spring通过反射调用afterThrowing接口,该接口可以被定义为:void afterThrowing(Method method, Object[] args, Object target, Throwable exception);

其中method、args和target是可选的,exception参数是必选的;在目标方法抛出异常后,实施增强。

 

除此之外,框架还定义了一种引介增强,用于在目标类中添加一些新的方法和属性。

 

4.5 案例介绍

4.5.1:前置、后置、环绕增强

定义前置通知:

@Slf4j
public class MyBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        LOGGER.info("----before----");
    }
}

 

定义后置通知:

@Slf4j
public class MyAfterReturningAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        LOGGER.info("----after----");
    }
}

 

 

定义环绕通知:

@Slf4j
public class MyRoundAdvice implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        LOGGER.info("====round before====");
        Object result = invocation.proceed();
        LOGGER.info("====round after====");
        return result;
    }
}

测试用例如下:

public class AdviceAopTest {
    @Test
    public void testAdvice() {
        ProxyFactory proxyFactory = new ProxyFactory();
        TaskService taskService = new TaskServiceImpl();
        proxyFactory.setTarget(taskService);
        proxyFactory.setInterfaces(TaskService.class);
        // 添加前置增强
        proxyFactory.addAdvice(new MyBeforeAdvice());
        // 添加后置增强
        proxyFactory.addAdvice(new MyAfterReturningAdvice());
        // 添加环绕增强
        proxyFactory.addAdvice(new MyRoundAdvice());
        // 获取代理对象
        TaskService proxy = (TaskService)proxyFactory.getProxy();
        proxy.sync();
    }
}

运行结果如下所示:

com.seong.demo.aop.advice.MyBeforeAdvice -----before--
com.seong.demo.aop.advice.MyRoundAdvice - ====round before====
com.seong.demo.aop.model.TaskServiceImpl -[sync data]
com.seong.demo.aop.advice.MyRoundAdvice -====round after====
com.seong.demo.aop.advice.MyAfterReturningAdvice -----after----

4.5.2:异常抛出增强

修改目标类代码逻辑:

@Slf4j
public class TaskServiceImpl implements TaskService{
    @Override
    @SneakyThrows
    public void sync() {
        LOGGER.info("[sync data]");
        throw new Exception("");
    }
}

测试用例如下:

public class ThrowsAdviceTest {
    @Test
    public void testAdvice() {
        ProxyFactory proxyFactory = new ProxyFactory();
        TaskService taskService = new TaskServiceImpl();
        proxyFactory.setTarget(taskService);
        proxyFactory.setInterfaces(TaskService.class);

        proxyFactory.addAdvice(new MyThrowsAdvice());
        TaskService proxy = (TaskService)proxyFactory.getProxy();
        proxy.sync();
    }
}

 

结果如下:

INF0 com.seong.demo.aop.model.TaskServiceImpl - [sync data]
ERROR com.seong.demo.aop.advice.MyThrowsAdvice-----Exception occurs----

5. 切点类型

框架定义切点是为了从目标类的连接点(执行点)中过滤出符合条件的部分,为此在切点类的内部提供类两个过滤器:ClassFilter和MethodMatcher,分别对类型和方法进行过滤。

public interface Pointcut {
	ClassFilter getClassFilter();

	MethodMatcher getMethodMatcher();

	// Pointcut.TRUE 对象表示所有目标类的所有方法均满足条件
	// (实例对应的ClassFilter和MethodMatcher对象的match方法均返回true)
	Pointcut TRUE = TruePointcut.INSTANCE;
}

Pointcut切点接口定义如上所示,Spring并基于此扩展出了多种切点类型;使得可以根据方法名、参数、是否包含注解以及表达式等进行过滤。

 

6. 切面类型

Spring使用Advisor表示切面类型,可以分为3类:一般切面Advisor、切点切面PointcutAdvisor、引介切面IntroductionAdvisor;一般切面Advisor仅包含一个Advice, 即表示作用对象是所有目标类的所有方法;PointcutAdvisor包含Advice和Pointcut信息,可以通过切点定位出满足Pointcut过滤条件的执行点集合;IntroductionAdvisor对应于引介切点和增强。

其中:PointcutAdvisor及其子类DefaultPointcutAdvisor是较为常见的切面类型,源码如下:

public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {

	private Pointcut pointcut = Pointcut.TRUE;
	
	private Advice advice = EMPTY_ADVICE;
	
	public DefaultPointcutAdvisor() {
	}

	public DefaultPointcutAdvisor(Advice advice) {
		this(Pointcut.TRUE, advice);
	}

	public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
		this.pointcut = pointcut;
		setAdvice(advice);
	}
}

 

DefaultPointcutAdvisor包含一个切点和一个增强类型属性:Pointcut的默认值为Pointcut.TRUE表示所有目标类的所有方法均为连接点;Advice的默认值为EMPTY_ADVICE:Advice EMPTY_ADVICE = new Advice() {};, 即表示不进行增强。

测试用例中为ProxyFactory添加切面部分逻辑为:proxyFactory.addAdvice(new MyBeforeAdvice());

等价于 proxyFactory.addAdvisor(new DefaultPointcutAdvisor(new MyBeforeAdvice()));.

 

 

 

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

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

相关文章

通用代码生成器模板体系,域对象,枚举和动词算子

通用代码生成器模板体系,域对象,枚举和动词算子 通用代码生成器或者叫动词算子式通用目的代码生成器是一组使用Java编写的通用代码生成器。它们的原理基于动词算子和域对象的笛卡尔积。它们没有使用FreeMarker和或者Velocity等现成的文件式模板引擎。而…

php快速入门

前言 php是一门脚本语言,可以访问服务器,对数据库增删查改(后台/后端语言) 后台语言:php,java,c,c,python等等 注意:php是操作服务器,不能直接在…

曝宝马汽车门店亏损严重价格战带来的伤害太大了

今年以来不仅餐饮行业难,就连一些车企都陷入困境当中,多家车企选择打价 格战。只不过日前的时候媒体爆料称,宝马汽车门店因为打价格战,最终亏损严 重,为了避免亏损再度出现,因此宝马7月将会开始降量保价。文章来源于:股城网www.gucheng.com 实际上,进入2024年…

分享语音音浪的自制动态特效

主要使用小程序实现的&#xff1a; <!-- wxml --> <view class"audio"><view class"audio-item" wx:for"{{list}}" wx:key"index" style"{{item}}"></view> </view> //js list: [animation…

Sentieon Arm版本:进一步降低基因组计算成本

前不久&#xff0c;Arm在其社区的HPC blog上发布了一篇Sentieon在低通量全基因组&#xff08;LP-WGS&#xff09;的应用案例。 图1 伴随着大规模基因组学的需求持续增长&#xff0c;基因测序成本的降低使得研究和分析更加广泛。而在基因组学的每一个应用背后,都有一系列计算密…

C++对顶堆(求第k大、k小数)+P1801题解

这里借鉴了一些别人题解的思路&#xff0c;仅供自己收藏使用。 题目大意&#xff1a;动态修改数组&#xff0c;求第k小的数。 from&#xff1a;7KByte P1801题解 对此&#xff0c;POISONN大佬发表了他的意见&#xff1a;楼主写法应该是让大根堆里有k-1个元素&#xff0c;然后…

如何从硬盘恢复已删除的视频

您是否想知道是否可以恢复已删除的视频&#xff1f; 幸运的是&#xff0c;您可以使用照片恢复从硬盘、SD 卡和 USB 闪存驱动器恢复已删除的视频文件。 您是否曾发生过这样的情况&#xff1a;当您随机删除文件以释放空间时&#xff0c;您不小心按下了一些重要视频的删除按钮并…

捷配生产笔记-细间距芯片的表面处理工艺:OSP与沉金工艺的重要性

在现代电子制造领域&#xff0c;随着技术的进步&#xff0c;电子设备变得越来越小型化和高性能化。细间距芯片作为实现这一目标的关键组件&#xff0c;其制造工艺要求极为严格。在这些要求中&#xff0c;表面处理工艺尤为关键&#xff0c;因为它直接影响到芯片的焊接质量和长期…

教你如何快速输入公式——Mathpix

概述 相信各位博主在撰写技术性博客时&#xff0c;都遇到在markdown编辑器中输入公式的问题&#xff0c;自己慢慢输入公式太过麻烦&#xff0c;想要直接复制文献中的又显示异常&#xff0c;是在叫人为难。这里&#xff0c;教大家使用一个只用截图&#xff0c;就能自动生成公式…

宁盾单点登录厂商-快速实现统一身份认证、单点登录SSO、MFA多因素认证

在当今数字化时代&#xff0c;企业的IT基础设施日益复杂&#xff0c;多系统、多设备的应用场景使得统一身份认证和单点登录&#xff08;SSO&#xff09;成为企业提升安全性和业务效率的关键需求。宁盾&#xff0c;作为数字身份基础设施提供商&#xff0c;凭借其单点登录&#x…

使用AutoGPT构建智能体:从LSTM到Prompt编写实战教程001

如果报错,这里会有一个环境变量的设置需要设置上. 然后这一节我们来自己制作一个智能体,来感受一下,实际上现在,大模型还是可以做很多功能的. 可以看到上面是智能体的架构,之前也说过了, 上面这几个功能,如果用我们人类去操作,还是需要花些时间的,如果用大模型就快很多了. 以…

研究人员利用浏览器渲染过程来改变 PDF 发票定价

首发公众号网络研究观&#xff0c;微信搜索关注每日获取更多内容。 网络安全研究员 Zakhar Fedotkin 演示了如何利用不同浏览器和操作系统之间 PDF 渲染的差异来操纵 PDF 发票上显示的价格。 此漏洞可能会严重影响依赖数字发票进行交易的企业。 研究人员受到 Konstantin Wed…

寻找并可视化交互

「AI秘籍」系列课程&#xff1a; 人工智能应用数学基础 人工智能Python基础 人工智能基础核心知识 人工智能BI核心知识 人工智能CV核心知识 使用特征重要性、弗里德曼 H 统计量和 ICE 图分析相互作用 本文中的代码需要安装 R 语言包 药物的副作用可能取决于你的性别。吸入…

SFUZZ模糊测试平台全新升级,从标准到实践助力车企安全出海

开源网安模糊测试平台SFuzz全新升级&#xff0c;参照各国相关标准要求进行针对性建设&#xff0c;可为智能网联汽车信息安全测试提供更为强大的工具支持。SFuzz向被测系统输入大量随机数据&#xff0c;模拟各种异常情况&#xff0c;可以发现被测系统内潜在的缺陷和漏洞&#xf…

从10个地产客户案例里,我们发现智能工牌在提升案场转化中的价值

地产竞争进入白热化阶段&#xff0c;面对获客转化困境&#xff0c;提升客户体验和到访转化率是房企必须着手的破局点。 到底如何把握成交前的最后一公里&#xff0c;深入剖析客户需求及抗性、复制销冠能力、最终提升案场转化率&#xff1f; 随着人工智能&#xff08;AI&#…

大前端热点技术

前言 2018年后&#xff0c;整个大前端发展趋于稳定&#xff0c;各大主流框架的特性变少&#xff0c;各种新轮子也在逐渐变少&#xff0c;但在多端融合、上下游提效以及一些细分领域&#xff0c;还是有很多值得期待的。 本文将基于过去一年大前端方向在Web、Node、多端、IoT、…

【数据结构】--- 堆

​ 个人主页&#xff1a;星纭-CSDN博客 系列文章专栏 :数据结构 踏上取经路&#xff0c;比抵达灵山更重要&#xff01;一起努力一起进步&#xff01; 目录 一.堆的介绍 二.堆的实现 1.向下调整算法 2.堆的创建 3.堆的实现 4.堆的初始化和销毁 5.堆的插入 5.1扩容…

动手学Avalonia:基于硅基流动构建一个文生图应用(一)

文生图 文生图&#xff0c;全称“文字生成图像”&#xff08;Text-to-Image&#xff09;&#xff0c;是一种AI技术&#xff0c;能够根据给定的文本描述生成相应的图像。这种技术利用深度学习模型&#xff0c;如生成对抗网络&#xff08;GANs&#xff09;或变换器&#xff08;T…

一个spring boot项目的启动过程分析

1、web.xml 定义入口类 <context-param><param-name>contextConfigLocation</param-name><param-value>com.baosight.ApplicationBoot</param-value> </context-param> 2、主入口类: ApplicationBoot,SpringBoot项目的mian函数 SpringBo…

“删错文件后如何高效挽救?两大恢复策略全解析“

在数字化日益深入生活的今天&#xff0c;数据已成为我们工作、学习和娱乐不可或缺的一部分。然而&#xff0c;删错文件的经历却如同数字世界中的一场“小插曲”&#xff0c;不经意间就可能让我们陷入数据丢失的困境。无论是误触删除键、清空回收站&#xff0c;还是软件故障导致…