【Spring】深入浅出SpringAOP

article/2025/8/15 18:49:25

SpringAOP

    • 前言
    • AOP基础
      • ProxyFactory
        • Pointcut
        • Advisor
        • ProxyFactory策略选择
        • JDK策略的执行流程
        • Cglib策略的执行流程
    • @EnableAspectJAutoProxy
    • AnnotationAwareAspectJAutoProxyCreator

前言

SpringAOP的原理是动态代理,动态代理又分为JDK动态代理Cglib动态代理,这些想必大家已经知道了,所以不再赘述动态代理相关内容,此处为大家提供两种代理的示例代码
JDK动态代理

    public static void jdkDynamicProxy() {UserServiceInterface userService = new UserService();UserServiceInterface proxyInstance  = (UserServiceInterface) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{UserServiceInterface.class}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("方法执行前");Object result = method.invoke(userService, args);System.out.println("方法执行后");return result;}});proxyInstance.test1();}

Cglib动态代理

    public static void cglibDynamicProxy() {UserService userService = new UserService();Enhancer enhancer =new Enhancer();enhancer.setSuperclass(UserService.class);enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("方法执行前");Object result = method.invoke(userService, objects);System.out.println("方法执行后");return result;}});UserService proxyBean = (UserService) enhancer.create();proxyBean.test1();}

AOP基础

ProxyFactory

在Spring中,不会直接使用JDK动态代理或者Cglib动态代理,而是基于这两种动态代理封装了一个更高级的功能:ProxyFactory

    public static void proxyFactoryDemo() {ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.setTarget(new UserService());proxyFactory.addAdvice((org.aopalliance.intercept.MethodInterceptor) invocation -> {System.out.println("方法执行前");Object result = invocation.proceed();System.out.println("方法执行后");return result;});UserService userService = (UserService) proxyFactory.getProxy();userService.test1();}

那么ProxyFactory有哪些好处呢?

  1. 不用关注底层具体实现,不需要操心是JDK动态代理还是Cglib动态代理
  2. TargetSource功能,对被代理对象封装,提供更丰富的操作
  3. 除了MethodInterceptor这一种Advice还有许多,例如AfterAdvice、AfterReturningAdvice
  4. 提供Pointcut功能,灵活配置切入点
Pointcut

Pointcut(切入点)配置了代理生效的范围:

  1. ClassFilter,哪些类生效
  2. MethodMatcher,哪些方法生效
    我们使用AspectJ时配置的Pointcut表达式最终就会被解析成Pointcut
Advisor

AdvicePointcut组成,Advice就是代理的逻辑内容。
可以往ProxyFactory中添加Advisor

        proxyFactory.addAdvisor(new Advisor() {@Overridepublic Advice getAdvice() {return null;}@Overridepublic boolean isPerInstance() {return false;}});

在项目启用AOP的前提下,我们往容器中注入Advisor,符合条件的方法就会被代理

@Component
public class CustomsAdvisor implements PointcutAdvisor {@Overridepublic Pointcut getPointcut() {return new Pointcut() {@Overridepublic ClassFilter getClassFilter() {return clazz -> {for (Class<?> aClass : clazz.getClasses()) {if(aClass.equals(OrderService.class)){return true;}}return false;};}@Overridepublic MethodMatcher getMethodMatcher() {return MethodMatcher.TRUE;}};}@Overridepublic Advice getAdvice() {return (MethodInterceptor) invocation -> {System.out.println("before");return invocation.proceed();};}@Overridepublic boolean isPerInstance() {return false;}
}
ProxyFactory策略选择

当调用ProxyFactory.getProxy时会走到org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy方法

	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine target class: " +"Either an interface or a target is required for proxy creation.");}if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}return new ObjenesisCglibAopProxy(config);}else {return new JdkDynamicAopProxy(config);}}
  1. 如果ProxyFactory设置isOptimize为true或者设置proxyTargetClass为true,则强制使用cglib代理
  2. 如果代理的目标类没有实现接口,使用cglib代理
  3. 如果代理的目标类就是一个接口,使用jdk代理
  4. 如果代理的目标类是Jdk生成的代理类,则使用jdk代理

简单来说,具体选择哪种策略主要看目标类有没有实现接口

JDK策略的执行流程
	public Object getProxy(@Nullable ClassLoader classLoader) {return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);}

传入的InvocationHandlerJdkDynamicAopProxy本身,所以我们看invoke方法
JdkDynamicAopProxy执行流程

  1. 如果当前方法是equals、hscode等,不执行代理逻辑
  2. 如果设置了exposeProxy,将代理对象设置到ThreadLocal中
			if (this.advised.exposeProxy) {// Make invocation available if necessary.oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}
  1. 获取针对当前方法符合条件的MethodIntercepter集合
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

a. 获取proxy中设置的所有Advisor
b. 根据ClassFilter和MethodMatcher过滤
c. AdvisorAdapterRegistry将Advisor进行适配,适配成MethodInterceptor
这个地方巧妙使用了适配器模式策略模式
AdvisorAdapterRegistry中注册了以下几种适配器

	public DefaultAdvisorAdapterRegistry() {registerAdvisorAdapter(new MethodBeforeAdviceAdapter());registerAdvisorAdapter(new AfterReturningAdviceAdapter());registerAdvisorAdapter(new ThrowsAdviceAdapter());}

可以将MethodBeforeAdviceAfterReturningAdviceThrowsAdvice都转换成MethodInterceptor

  1. 将执行链封装成ReflectiveMethodInvocation,非常巧妙的使用递归完成整个调用链的传递
  2. 在执行MethodInterceptor的invoke方法时,将本身传了进去
Cglib策略的执行流程
  1. 创建Enhancer对象
  2. 设置suplierClass为代理的类
  3. 设置Callback为DynamicAdvisedInterceptor
  4. 当执行代理方法时会进入到DynamicAdvisedInterceptorintercept方法

@EnableAspectJAutoProxy

@EnableAspectJAutoProxy注解会Import一个配置类AspectJAutoProxyRegistrar,这个配置类实现了ImportBeanDefinitionRegistrar接口,向容器中注册了AnnotationAwareAspectJAutoProxyCreator的Bean

AnnotationAwareAspectJAutoProxyCreator

AnnotationAwareAspectJAutoProxyCreator是一个BeanPostProcessor,在初始化后方法postProcessAfterInitialization中,如果必要会对当前Bean生成代理对象
在这里插入图片描述

	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {// 表示已经生成过代理,跳过处理if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {return bean;}// advisedBeans表示已经判断过了的bean,false表示此bean不需要进行Aopif (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}// 如果是AOP基础设施Bean,不用代理if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}// Create proxy if we have advice.// 判断当前bean是否存在匹配的advice,如果存在则要生成一个代理对象// 此处根据类以及类中的方法去匹配到Interceptor(也就是Advice),然后生成代理对象,代理对象在执行的时候,还会根据当前执行的方法去匹配Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);if (specificInterceptors != DO_NOT_PROXY) {// advisedBeans记录了某个Bean已经进行过AOP了this.advisedBeans.put(cacheKey, Boolean.TRUE);// 创建代理Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}

AbstractAutoProxyCreator这个类为Spring提供了AOP功能,使其能够处理容器中的Advisor;
AnnotationAwareAspectJAutoProxyCreator这个类则提供了解析AspectJ注解的能力,使其能够将容器中的切面解析成Advisor


http://www.hkcw.cn/article/LTuExomULT.shtml

相关文章

白玉兰入围奖项全名单 各单元角逐揭晓在即

第30届上海电视节白玉兰奖海外电视剧单元的入围名单已经公布,颁奖典礼将于6月27日举行。至此,本届白玉兰奖的所有入围名单已全部发布,包括中国电视剧单元、纪录片、动画和综艺类别。之前公布的名单中,有10部作品将角逐中国电视剧单元奖项,15部综艺节目也入围了综艺类别的评…

我国北部将有机会出现极光 太阳活动引发期待

北京时间5月31日07时45分左右,太阳活动区14100开始爆发耀斑,软X射线流量快速上升,并在08时05分达到M8.1级中等耀斑强度。中国气象局国家空间天气监测预警中心预判,未来三天可能发生地磁暴。2日左右,我国北部有机会出现较为明显的极光,部分地区甚至可能出现红绿复合极光。…

Hailo8教程笔记

Hailo8教程笔记 Tappas系统整合测试工具模型转换工具资料下载自己安装记录Hailo AI Software Suite – Docker(完整开发环境Docker镜像安装)TAPPAS安装教程安装基础环境OpenCV安装 TAPPAS 所需系统依赖项安装Tappashttps://github.com/hailo-ai/hailort Tappas 系统整合测试工…

马斯克为何离开白宫核心圈子 不再当“替罪羊”

当地时间5月28日,马斯克在社交媒体上宣布其作为特朗普政府“特殊雇员”的任期结束,白宫随即启动离职程序。正值SpaceX星舰基地迈向星辰大海之际,他在白宫的短暂生涯却在争议与抵制中告终。马斯克曾誓言重塑政府效率,最终以“替罪羊”自嘲,从华盛顿的权力漩涡中抽身而退。短…

pikachu通关教程-XSS

XSS XSS漏洞原理 XSS被称为跨站脚本攻击&#xff08;Cross Site Scripting&#xff09;&#xff0c;由于和层叠样式表&#xff08;Cascading Style Sheets&#xff0c;CSS&#xff09;重名&#xff0c;改为XSS。主要基于JavaScript语言进行恶意攻击&#xff0c;因为js非常灵活…

加拿大野火烟尘污染美国 邻国空气质量恶化

近日,加拿大多地野火肆虐,产生的烟尘随风向南扩散,影响了相邻的美国一些州,导致空气污染。据美国环境保护署空气质量监测网站AirNow消息,5月30日上午,明尼苏达州阿罗黑德市一带的空气质量恶化为“橙色”。美国环保署将空气质量从最佳到最差分为六级,依次用绿色、黄色、橙…

卡友跨2400公里送缺氧离世司机回家 爱心接力暖人心

46岁的河南卡车司机常志荣在青藏线因高原缺氧离世。今天上午,他的骨灰及车辆被多名爱心卡友从五道梁地区送回老家安阳林州。5月27日,常志荣在青藏线五道梁地区遭遇严重缺氧,不幸去世。车友任先生透露,出发前同行曾建议他至少携带两罐氧气,但他为了省下30元费用,最终只买了…

以军称打死哈马斯指挥官哈利勒 曾致21名士兵死亡

以色列国防军于当地时间6月1日发布消息称,一名哈马斯指挥官近日被以军打死。这名指挥官名叫哈利勒阿卜杜勒,是哈马斯马瓦西营的成员。他在5月30日的一次联合行动中被击毙,该行动由以色列国防军和以色列安全局共同执行。据以军透露,哈利勒指挥的武装团伙曾在2024年1月22日向…

数据结构(Java版)第四期:ArrayLIst和顺序表(上)

目录 一、顺序表 1.1. 接口的实现 二、ArrayList简介 2.1. ArrayList的构造 2.2. ArrayList的常见操作 2.3. ArrayList的扩容机制 三、ArrayList的具体使用 3.1. 洗牌算法 3.2. 杨辉三角 一、顺序表 上一期我们讲到过&#xff0c;顺序表本质上和数组是差不多的&#…

【Qt】:Dialog 对话框

&#x1f4c3;个人主页&#xff1a;island1314 ⛺️ 欢迎关注&#xff1a;&#x1f44d;点赞 &#x1f442;&#x1f3fd;留言 &#x1f60d;收藏 &#x1f49e; &#x1f49e; &#x1f49e; 生活总是不会一帆风顺&#xff0c;前进的道路也不会永远一马平川&#xff0c;如何面…

【C++】详解vector二维数组的全部操作(超细图例解析!!!)

目录 一、前言 二、 深度理解vector 的二维数组&#xff08;重点&#xff01;&#xff09; 三、vector 二维数组的空间理解&#xff08;重点&#xff01;&#xff09; ✨问题分析 ✨如何合理定制vector的内存空间 四、vector 二维数组的初始化 五、vector 二维数组的 添加…

已解决 javax.xml.transform.TransformerFactoryConfigurationError 异常的正确解决方法,亲测有效!!!

已解决 javax.xml.transform.TransformerFactoryConfigurationError 异常的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 一、问题分析 二、报错原因 三、解决思路 四、解决方法 五、总结 博主v&#xff1a;XiaoMing_Java 博主v&#x…

【Python】FastAPI入门

文章目录 第一节&#xff1a;FastAPI入门一、FastAPI框架介绍什么是ASGI服务&#xff08;WSGI&#xff09;1、补充Web开发1&#xff09;Web前端开发2&#xff09;Web后端开发 二、FastAPI安装1、安装Python虚拟环境2、安装FastAPI 三、第一个FastAPI案例1、访问接口和文档2、接…

【C++】踏上C++学习之旅(四):细说“内联函数“的那些事

文章目录 前言1. "内联函数"被创造出来的意义2. 内联函数的概念2.1 内联函数在代码中的体现2.2 普通函数和内联函数的汇编代码 3. 内联函数的特性&#xff08;重点&#xff09;4. 总结 前言 本章来聊一聊C的创作者"本贾尼"大佬&#xff0c;为什么要创作出…

C++《string的模拟实现》

在之前的篇章C《string》中我们已经了解了string中关于构造、容量、访问、修改操作等函数的功能&#xff0c;以及初步学习了这些函数的使用该如何使用。通过学习string内的各个函数后我们可以发现在解决一些要使用到字符串的环境下有了string内的这些函数操作能大大简化&#x…

2025华为OD机试真题最新题库 (B+C+D+E+2025A+2025B卷) + 在线OJ在线刷题使用(C++、Java、Python C语言 JS合集)(正在更新2025B卷,目前已收录710道)

2025年&#xff0c;已经开始使用AB卷题库&#xff0c;题目和往期一样&#xff0c;旧题加新题的组合&#xff0c;有题目第一时间更新&#xff0c;大家可以跟着继续学习&#xff0c;目前使用复用题较多&#xff0c;可在OJ上直接找到对应的AB卷学习&#xff0c;可以放心学习&#…

基于Java的不固定长度字符集在指定宽度和自适应模型下图片绘制生成实战

目录 前言 一、需求介绍 1、指定宽度生成 2、指定列自适应生成 二、Java生成实现 1、公共方法 2、指定宽度生成 3、指定列自适应生成 三、总结 前言 在当今数字化与信息化飞速发展的时代&#xff0c;图像的生成与处理技术正日益成为众多领域关注的焦点。从创意设计到数…

【C++】 —— 笔试刷题day_29

一、排序子序列 题目解析 一个数组的连续子序列&#xff0c;如果这个子序列是非递增或者非递减的&#xff1b;这个连续的子序列就是排序子序列。 现在给定一个数组&#xff0c;然后然我们判断这个子序列可以划分成多少个排序子序列。 例如&#xff1a;1 2 3 2 2 1 可以划分成 …

企业微信自建应用实现接收消息和发送消息功能(python)

# 这一周我不断的琢磨企业微信自建应用并且实现了自建应用的消息接收和发送功能 1.笔记&#xff0c;记录 第一步&#xff1a;打开企业微信后台 https://work.weixin.qq.com 1.1 如果没有企业可以在这里申请&#xff0c;如果有可以直接扫码登录 1.2 打开后台-应用管理-自建应用…

【Rust多线程】Rust并发编程,如何轻松实现无畏并发

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…