当前位置: 首页 > news >正文

【JavaEE进阶】 Spring AOP源码简单剖析

文章目录

  • 🍃前言
  • 🍀Spring AOP源码剖析
  • ⭕总结

🍃前言

前面的博客中,博主对代理模式进行了一个简单的讲解,接下来博主将对Spring AOP源码进行简单剖析,使我们对Spring AOP了解的更加深刻。

🍀Spring AOP源码剖析

Spring AOP 主要基于两种⽅式实现的:JDK 及 CGLIB 的⽅式

Spring对于AOP的实现,基本上都是靠AnnotationAwareAspectJAutoProxyCreator 去完成⽣成代理对象的逻辑在⽗类 AbstractAutoProxyCreator 中

相关源码与注解如下:

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,@Nullable Object[] specificInterceptors, TargetSource targetSource) {if (this.beanFactory instanceof ConfigurableListableBeanFactory) {AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)this.beanFactory, beanName, beanClass);}
//创建代理⼯⼚ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);
/*** 检查proxyTargetClass属性值,spring默认为false* proxyTargetClass 检查接⼝是否对类代理, ⽽不是对接⼝代理* 如果代理对象为类, 设置为true, 使⽤cglib代理*/if (!proxyFactory.isProxyTargetClass()) {
//是否有设置cglib代理if (shouldProxyTargetClass(beanClass, beanName)) {
//设置proxyTargetClass为true,使⽤cglib代理proxyFactory.setProxyTargetClass(true);} else {
/*** 如果beanClass实现了接⼝,且接⼝⾄少有⼀个⾃定义⽅法,则使⽤JDK代理* 否则CGLIB代理(设置ProxyTargetClass为true )* 即使我们配置了proxyTargetClass=false, 经过这⾥的⼀些判断还是可能会将其设为true*/evaluateProxyInterfaces(beanClass, proxyFactory);}}Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);proxyFactory.addAdvisors(advisors);proxyFactory.setTargetSource(targetSource);customizeProxyFactory(proxyFactory);proxyFactory.setFrozen(this.freezeProxy);if (advisorsPreFiltered()) {proxyFactory.setPreFiltered(true);}
// Use original ClassLoader if bean class not locally loaded in overridingclass loader
ClassLoader classLoader = getProxyClassLoader();if (classLoader instanceof SmartClassLoader && classLoader !=beanClass.getClassLoader()) {classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();}
//从代理⼯⼚中获取代理return proxyFactory.getProxy(classLoader);
}

代理工⼚有⼀个重要的属性:proxyTargetClass,默认值为false.

也可以通过程序设置

proxyTargetClass⽬标对象代理⽅式
false实现了接⼝jdk代理
false未实现接⼝(只有实现类)cglib代理
true实现了接⼝cglib代理
true未实现接⼝(只有实现类)cglib代理

可以通过 @EnableAspectJAutoProxy(proxyTargetClass = true) 来设置

需要注意的是:

  • Spring Boot 2.X开始,默认使⽤CGLIB代理,可以通过配置项 spring.aop.proxy-target-class=false 来进⾏修改,设置默认为jdk代理
    SpringBoot设置 @EnableAspectJAutoProxy ⽆效,因为Spring Boot默认使⽤AopAutoConfiguration进⾏装配
@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {ApplicationContext context =SpringApplication.run(DemoApplication.class, args);
/*** HouseProxy houseProxy = context.getBean(HouseProxy.class);* 设置spring.aop.proxy-target-class=true cglib代理, 运⾏成功* 设置spring.aop.proxy-target-class=false jdk代理, 运⾏失败, 不能代理类* 因为 HouseProxy 是⼀个类, ⽽不是接⼝, 需要修改为* HouseSubject houseProxy = (HouseSubject)context.getBean("realHouseSubject")**/HouseProxy houseProxy = context.getBean(HouseProxy.class);
//HouseSubject houseProxy = (HouseSubject)context.getBean("realHouseSubject");//正确运⾏System.out.println(houseProxy.getClass().toString());}
}

注意:使⽤context.getBean()需要添加注解,使HouseProxy,RealHouseSubject被Spring管理测试AOP代理,需要把这些类交给AOP管理(⾃定义注解或使用@Aspect)

我们再看以下代理⼯⼚的代码

public class ProxyFactory extends ProxyCreatorSupport {//...代码省略
//获取代理public Object getProxy(@Nullable ClassLoader classLoader) {
//分两步 先createAopProxy,后getProxyreturn createAopProxy().getProxy(classLoader);}protected final synchronized AopProxy createAopProxy() {if (!this.active) {activate();}return getAopProxyFactory().createAopProxy(this);}
//...代码省略
}

createAopProxy的实现在DefaultAopProxyFactory中

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {//...代码省略@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throwsAopConfigException {
/*** 根据proxyTargetClass判断* 如果⽬标类是接⼝, 使⽤JDK动态代理* 否则使⽤cglib动态代理*/if (!NativeDetector.inNativeImage() &&(config.isOptimize() || config.isProxyTargetClass() ||hasNoUserSuppliedProxyInterfaces(config))) {Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determinetarget class: " +"Either an interface or a target is required for proxycreation.");}if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) ||ClassUtils.isLambdaClass(targetClass)) {return new JdkDynamicAopProxy(config);}return new ObjenesisCglibAopProxy(config);}else {return new JdkDynamicAopProxy(config);}}
//...代码省略
}

接下来就是创建代理了

JDK动态代理

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler,Serializable {//...代码省略@Overridepublic Object getProxy(@Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace("Creating JDK dynamic proxy: " +this.advised.getTargetSource());}return Proxy.newProxyInstance(determineClassLoader(classLoader),this.proxiedInterfaces, this);}
//...代码省略
}

CGLIB动态代理

class CglibAopProxy implements AopProxy, Serializable {//...代码省略@Overridepublic Object getProxy(@Nullable ClassLoader classLoader) {//...代码省略
// Configure CGLIB Enhancer...Enhancer enhancer = createEnhancer();
// Generate the proxy class and create a proxy instance.return createProxyClassAndInstance(enhancer, callbacks);}
//...代码省略
}

以上就是对Spring AOP源码的一个简单剖析。

⭕总结

关于《【JavaEE进阶】 Spring AOP源码简单剖析》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下

相关文章:

【JavaEE进阶】 Spring AOP源码简单剖析

文章目录 &#x1f343;前言&#x1f340;Spring AOP源码剖析⭕总结 &#x1f343;前言 前面的博客中&#xff0c;博主对代理模式进行了一个简单的讲解&#xff0c;接下来博主将对Spring AOP源码进行简单剖析&#xff0c;使我们对Spring AOP了解的更加深刻。 &#x1f340;Sp…...

Redis--内存回收机制详解

什么是内存回收机制? 众所周知Redis之所以性能高是因为数据都存在内存中&#xff0c;内存是很宝贵的&#xff0c;Redis的内存回收机制本质就是处理达到过期时间的key-value&#xff0c;以及当内存到达最大使用值时候触发的内存淘汰策略。 Redis数据删除的策略有哪些&#xf…...

win安装卸载python3.13

一、安装 访问python官网&#xff1a;https://www.python.org/ 点击“Downloads” 点击“Windows” 找到自己要下载的版本和位数&#xff0c;比如我这个是3.13版本、64位的安装包 下载好了之后&#xff0c;双击安装包 勾选“Add python.exe to PATH”&#xff1a;把python环…...

APIFox-自动获取登录状态操作

APIFox-自动获取登录状态操作 概述 作为纯后端开发码农&#xff0c;每次接口开发完的调试很重要&#xff0c;因此每次重复的手动获取登陆状态Token或者直接放行就太麻烦了。 APIFox提供了前置操作&#xff0c;可以很方便的自动获取登录状态&#xff0c;节省大量重复劳动时间。…...

【NDK系列】Android tombstone文件分析

文件位置 data/tombstone/tombstone_xx.txt 获取tombstone文件命令&#xff1a; adb shell cp /data/tombstones ./tombstones 触发时机 NDK程序在发生崩溃时&#xff0c;它会在路径/data/tombstones/下产生导致程序crash的文件tombstone_xx&#xff0c;记录了死亡了进程的…...

CentOS7 Hive2.3.8安装

CentOS7 Hive2.3.8 安装 建议从头用我的博客&#xff0c;如果用外教的文件到 一、9)步骤了&#xff0c;就用他的弄完&#xff0c;数据库不一样&#xff0c;在9步骤前还能继续看我的 一、 安装MySQL 0.0&#xff09;查询mariadb,有就去0.1&#xff09;&#xff0c;没有就不管…...

代码随想录算法训练营第四十四天 完全背包 、零钱兑换 II 、组合总和 Ⅳ

代码随想录算法训练营第四十四天 | 完全背包 、零钱兑换 II 、组合总和 Ⅳ 完全背包 题目链接&#xff1a;题目页面 (kamacoder.com) 解释一、01背包 一维 &#xff1a;为什么要倒序遍历背包&#xff1f; 首先要明白二维数组的递推过程&#xff0c;然后才能看懂二维变一维的…...

【经验】vscode 鼠标拖曳不能选中整行文字,只能选中纵向矩形范围

1、问题描述 不知道昨天操作vscode设置界面时&#xff0c;误选择了啥&#xff0c;导致鼠标拖曳不能选中整行文字&#xff0c;只能选中纵向矩形范围&#xff0c;现象如下&#xff1a; 2、解决方法 1&#xff09;打开设置界面 点击左下角按键&#xff0c;选择“设置” 2&…...

Redis--事务机制的详解及应用

Redis事务的概念&#xff1a; Redis事务就是将一系列命令包装成一个队列&#xff0c;在执行时候按照添加的顺序依次执行&#xff0c;中间不会被打断或者干扰&#xff0c;在执行事务中&#xff0c;其他客户端提交的命令不可以插入到执行事务的队列中&#xff0c;简单来说Redis事…...

路由器端口映射如何配置?

在网络通信中&#xff0c;路由器是一个重要的设备&#xff0c;它负责将数据包从一个网络传输到另一个网络。路由器的端口映射配置是一种重要的设置&#xff0c;可以使外部网络中的计算机通过访问路由器上的特定端口与内部网络中的计算机进行通信。本文将介绍什么是路由器端口映…...

力扣34. 在排序数组中查找元素的第一个和最后一个位置(二分查找)

Problem: 34. 在排序数组中查找元素的第一个和最后一个位置 文章目录 题目描述思路复杂度Code 题目描述 思路 Problem: 二分查找常用解题模板&#xff08;带一道leetcode题目&#xff09; 直接套用上述中的寻找左、右边界的二分查找模板即可 复杂度 时间复杂度: O ( l o g n )…...

【每日一题】3.2 求逆序对

题目描述 给定一个长度为 n的整数数列&#xff0c;请你计算数列中的逆序对的数量。 逆序对的定义如下&#xff1a;对于数列的第 i个和第 j个元素&#xff0c;如果满足 i<j 且 a[i]>a[j]&#xff0c;则其为一个逆序对&#xff1b;否则不是。 输入格式 第一行包含整数 n…...

NTP时间源服务器(NTP网络时钟)助力智慧医院数字化

NTP时间源服务器&#xff08;NTP网络时钟&#xff09;助力智慧医院数字化 NTP时间源服务器&#xff08;NTP网络时钟&#xff09;助力智慧医院数字化 目前计算机网络中各主机和服务器等网络设备的时间基本处于无序的状态。 随着计算机网络应用的不断涌现&#xff0c;计算机的时…...

Benchmark学习笔记

小记一篇Benchmark的学习笔记 1.什么是benchmark 在维基百科中&#xff0c;是这样子讲的 “As computer architecture advanced, it became more difficult to compare the performance of various computer systems simply by looking at their specifications.Therefore, te…...

Linux中的动静态库

目录 一、静态库 &#xff08;1&#xff09;静态库的优缺点&#xff1a; &#xff08;2&#xff09;Linux下静态库的创建和执行 1.直接编译​编辑 2.指定路径和库名 3.用LIBRARY_PATH环境变量来配置路径 二、动态库 &#xff08;1&#xff09;动态库的优缺点 &#xff…...

C/C++基础语法

C/C基础语法 文章目录 C/C基础语法头文件经典问题链表链表基础操作 秒数转换闰年斐波那契数列打印n阶菱形曼哈顿距离菱形图案的定义大数计算 输入输出格式化输入输出getline()函数解决cin只读入一个单词的问题fgets读入整行输出字符数组&#xff08;两种方式puts和printf&#…...

Home Assistant:基于Python的智能家居开源系统详解

Home Assistant&#xff1a;基于Python的智能家居开源系统详解 在数字化和智能化的时代&#xff0c;智能家居系统成为了现代家庭的新宠。它们能够让我们更加方便地控制家中的各种设备&#xff0c;实现自动化和个性化的居住体验。其中&#xff0c;Home Assistant作为一款基于Pyt…...

使用vscode进行简单的多文件编译

安装好必要的插件后&#xff08;如C/C&#xff0c;code runner等&#xff09;默认生成task.json即可进行单文件运行 涉及到多文件情况可以修改task.json如下&#xff1a; {"version": "2.0.0","tasks": [{"type": "cppbuild&quo…...

Python实现PPT演示文稿中视频的添加、替换及提取

无论是在教室、会议室还是虚拟会议中&#xff0c;PowerPoint 演示文稿都已成为一种无处不在的工具&#xff0c;用于提供具有影响力的可视化内容。PowerPoint 提供了一系列增强演示的功能&#xff0c;在其中加入视频的功能可以大大提升整体体验。视频可以传达复杂的概念、演示产…...

Mysql学习之MVCC解决读写问题

多版本并发控制 什么是MVCC MVCC &#xff08;Multiversion Concurrency Control&#xff09;多版本并发控制。顾名思义&#xff0c;MVCC是通过数据行的多个版本管理来实现数据库的并发控制。这项技术使得在InnoDB的事务隔离级别下执行一致性读操作有了保证。换言之&#xff0…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving

地址&#xff1a;LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂&#xff0c;正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...

API网关Kong的鉴权与限流:高并发场景下的核心实践

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言 在微服务架构中&#xff0c;API网关承担着流量调度、安全防护和协议转换的核心职责。作为云原生时代的代表性网关&#xff0c;Kong凭借其插件化架构…...

FFmpeg avformat_open_input函数分析

函数内部的总体流程如下&#xff1a; avformat_open_input 精简后的代码如下&#xff1a; int avformat_open_input(AVFormatContext **ps, const char *filename,ff_const59 AVInputFormat *fmt, AVDictionary **options) {AVFormatContext *s *ps;int i, ret 0;AVDictio…...

TJCTF 2025

还以为是天津的。这个比较容易&#xff0c;虽然绕了点弯&#xff0c;可还是把CP AK了&#xff0c;不过我会的别人也会&#xff0c;还是没啥名次。记录一下吧。 Crypto bacon-bits with open(flag.txt) as f: flag f.read().strip() with open(text.txt) as t: text t.read…...

GeoServer发布PostgreSQL图层后WFS查询无主键字段

在使用 GeoServer&#xff08;版本 2.22.2&#xff09; 发布 PostgreSQL&#xff08;PostGIS&#xff09;中的表为地图服务时&#xff0c;常常会遇到一个小问题&#xff1a; WFS 查询中&#xff0c;主键字段&#xff08;如 id&#xff09;莫名其妙地消失了&#xff01; 即使你在…...