.net 使用IL生成代理类实现AOP对比Java Spring Boot的AOP
首先,我们需要定义一个接口,代表我们要代理的目标对象的功能:
// 日志记录器接口
public interface ILogger
{/// <summary>/// 记录日志/// </summary>/// <param name="message">日志消息</param>void Log(string message);
}// 日志记录器实现类
public class Logger : ILogger
{public void Log(string message){Console.WriteLine($"Logging: {message}");}
}
然后,我们创建一个代理类,实现该接口,并在目标方法的执行前后注入额外的逻辑:
// 切面接口
public interface IInterceptor
{/// <summary>/// 在方法执行前执行的逻辑/// </summary>/// <param name="targetType">目标类型</param>/// <param name="methodName">方法名称</param>void BeforeMethod(Type targetType, string methodName);/// <summary>/// 在方法执行后执行的逻辑/// </summary>/// <param name="targetType">目标类型</param>/// <param name="methodName">方法名称</param>void AfterMethod(Type targetType, string methodName);
}// 日志记录切面类
public class LoggingAspect : IInterceptor
{public void BeforeMethod(Type targetType, string methodName){Console.WriteLine($"Before {targetType.Name}.{methodName}");}public void AfterMethod(Type targetType, string methodName){Console.WriteLine($"After {targetType.Name}.{methodName}");}
}
接下来,我们通过使用IL生成代理类型的字节码,动态创建代理对象:
// 代理生成器
public static class ProxyGenerator
{/// <summary>/// 创建代理对象/// </summary>/// <typeparam name="TInterface">目标接口类型</typeparam>/// <param name="target">目标对象实例</param>/// <param name="interceptor">切面对象实例</param>/// <returns>代理对象</returns>public static TInterface CreateProxy<TInterface>(TInterface target, IInterceptor interceptor)where TInterface : class{Type targetType = typeof(TInterface);TypeBuilder typeBuilder = CreateTypeBuilder(targetType);ImplementInterface(typeBuilder, targetType);ImplementMethods(typeBuilder, targetType, interceptor);Type proxyType = typeBuilder.CreateType();return Activator.CreateInstance(proxyType, target, interceptor) as TInterface;}private static TypeBuilder CreateTypeBuilder(Type targetType){string typeName = targetType.Name + "Proxy";AssemblyName assemblyName = new AssemblyName(typeName);AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(typeName + "Module");TypeBuilder typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class);typeBuilder.AddInterfaceImplementation(targetType);return typeBuilder;}private static void ImplementInterface(TypeBuilder typeBuilder, Type targetType){foreach (MethodInfo method in targetType.GetMethods()){Type[] parameterTypes = method.GetParameters().Select(p => p.ParameterType).ToArray();MethodBuilder methodBuilder = typeBuilder.DefineMethod(method.Name,MethodAttributes.Public | MethodAttributes.Virtual,method.ReturnType,parameterTypes);typeBuilder.DefineMethodOverride(methodBuilder, method);}}private static void ImplementMethods(TypeBuilder typeBuilder, Type targetType, IInterceptor interceptor){foreach (MethodInfo method in targetType.GetMethods()){MethodBuilder methodBuilder = typeBuilder.GetMethod(method.Name).GetBaseDefinition() as MethodBuilder;if (methodBuilder != null){ILGenerator ilGenerator = methodBuilder.GetILGenerator();// 调用切面方法ilGenerator.Emit(OpCodes.Ldarg_0); // 加载this到堆栈ilGenerator.Emit(OpCodes.Ldfld, typeBuilder.GetField("interceptor")); // 加载interceptor到堆栈ilGenerator.Emit(OpCodes.Ldarg_0); // 加载this到堆栈ilGenerator.Emit(OpCodes.Ldtoken, targetType); // 加载targetType到堆栈ilGenerator.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle")); // 调用GetTypeFromHandle方法ilGenerator.Emit(OpCodes.Ldstr, method.Name); // 加载方法名到堆栈ilGenerator.Emit(OpCodes.Callvirt, typeof(IInterceptor).GetMethod("BeforeMethod")); // 调用BeforeMethod方法ilGenerator.Emit(OpCodes.Pop); // 丢弃返回值// 调用目标方法ilGenerator.Emit(OpCodes.Ldarg_0); // 加载this到堆栈for (int i = 1; i <= method.GetParameters().Length; i++){ilGenerator.Emit(OpCodes.Ldarg_S, i); // 加载方法参数到堆栈}ilGenerator.Emit(OpCodes.Callvirt, targetType.GetMethod(method.Name)); // 调用目标方法// 调用切面方法ilGenerator.Emit(OpCodes.Ldarg_0); // 加载this到堆栈ilGenerator.Emit(OpCodes.Ldfld, typeBuilder.GetField("interceptor")); // 加载interceptor到堆栈ilGenerator.Emit(OpCodes.Ldarg_0); // 加载this到堆栈ilGenerator.Emit(OpCodes.Ldtoken, targetType); // 加载targetType到堆栈ilGenerator.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle")); // 调用GetTypeFromHandle方法ilGenerator.Emit(OpCodes.Ldstr, method.Name); // 加载方法名到堆栈ilGenerator.Emit(OpCodes.Callvirt, typeof(IInterceptor).GetMethod("AfterMethod")); // 调用AfterMethod方法ilGenerator.Emit(OpCodes.Pop); // 丢弃返回值}}}
}
最后,我们可以使用以下代码来测试动态代理的功能:
class Program
{static void Main(string[] args){// 创建目标对象ILogger logger = new Logger();// 创建切面对象IInterceptor interceptor = new LoggingAspect();// 创建代理对象ILogger proxy = ProxyGenerator.CreateProxy(logger, interceptor);// 调用代理对象的方法proxy.Log("Hello, World!");Console.ReadLine();}
}
对比一下Java Spring Boot 的AOP
-
动态代理实现方式:在Java Spring Boot中,基于代理的AOP主要使用JDK动态代理和CGLIB代理来实现。而在C#中,使用IL生成器(ILGenerator)直接操作IL指令来生成和修改类型的字节码,实现动态代理。这种方式相对于代理类的生成更加底层,需要对CLR(Common Language Runtime)和IL指令有一定的了解。
-
IL的语法和特性:IL是.NET平台的中间语言,类似于汇编语言,但具有一些.NET特定的语法和特性。IL指令用于描述类型、方法、属性等的结构和操作,需要了解这些指令的使用规则和约束。相比之下,Java字节码(Java bytecode)是Java虚拟机(JVM)上的中间语言,其语法和特性与IL有所不同。
-
第三方库和框架:在Java生态系统中,有许多第三方库和框架(如AspectJ、Spring AOP)提供了高级别的API和工具,使AOP的使用更加方便。而在C#中,虽然也有一些库(如Castle DynamicProxy、Unity Interception)可以辅助实现AOP,但相对于Java生态系统来说,可选择的工具和框架较少
综上所述,C#使用IL实现AOP与Java Spring Boot的AOP在实现方式、编程语言和生态系统等方面存在一些不同。C#开发者需要直接操作IL指令来生成和修改类型的字节码,需要对CLR和IL指令有一定的了解。而Java Spring Boot的AOP则基于代理实现,使用注解和切点表达式等高级别的API来配置和管理AOP。
相关文章:
.net 使用IL生成代理类实现AOP对比Java Spring Boot的AOP
首先,我们需要定义一个接口,代表我们要代理的目标对象的功能: // 日志记录器接口 public interface ILogger {/// <summary>/// 记录日志/// </summary>/// <param name"message">日志消息</param>void L…...

美容店预约小程序搭建流程
随着科技的不断发展,小程序已经成为了人们生活中不可或缺的一部分。对于美容店来说,搭建一个预约小程序不仅可以提高工作效率,还可以增加客户数量、提高服务质量。那么,如何搭建一个美容店预约小程序呢?本文将为你详细…...

ppt 作图 如何生成eps格式
需求 ppt中画的图,按照eps格式导出。 环境 软件: ppt, Gsview(用来将ps格式转成eps), Adobe 操作系统: win11 思路 直接在ppt里选择adobe打印机,将图片以文件形式打印到ps格式的文件中,再由gsview转化成eps。 建议在本身就…...

渗透测试中的前端调试(上)
一、前言 前端调试是安全测试的重要组成部分。它能够帮助我们掌握网页的运行原理,包括js脚本的逻辑、加解密的方法、网络请求的参数等。利用这些信息,我们就可以更准确地发现网站的漏洞,制定出有效的攻击策略。前端知识对于安全来说ÿ…...

跨境电商引流之Reddit营销,入门保姆级攻略
在当今竞争激烈的在线市场中,企业不断寻求新的方法来加强其数字营销工作。Reddit 是最受欢迎的社交媒体平台之一,为企业提供了巨大的潜力,可以通过引人入胜且相关的内容来接触目标受众。然而,将 Reddit 用于营销目的需要仔细考虑某…...
Linux下虚拟网卡的基本命令
文章目录 创建虚拟网卡查看虚拟网卡删除虚拟网卡 创建虚拟网卡 # 创建tap模式的虚拟网卡tap0 sudo ip tuntap add mode tap tap0 # 开启网卡 sudo ip link set tap0 up # 设置网卡的ip地址和子网掩码 sudo ip addr add 192.168.1.1/24 dev tap0查看虚拟网卡 # 查看虚拟网卡ta…...

conan入门(二十七):因profile [env]字段废弃导致的boost/1.81.0 在aarch64-linux-gnu下交叉编译失败
今天在尝试用conan 1.60.0使用aarch64-linux-gnu编译器交叉编译boost/1.81.0时报错了: conan install boost/1.81.0 -pr:h aarch64-linux-gnu.jinja -pr:b default --build boost输出如下: Configuration (profile_host): [settings] archarmv8 arch_b…...

BFS专题7 多终点迷宫问题
题目: 样例: 输入 3 3 0 0 0 1 0 0 0 1 0 输出 0 1 2 -1 2 3 -1 -1 4 思路: 单纯的 BFS 迷宫问题,只是标记一下每个点的 step,注意初始化答案数组都为 -1. 代码详解如下: #include <iostream> #…...
ES6中对象新增了哪些扩展?
一、属性的简写 当对象字面量的属性名与变量名相同时,可以省略属性名,直接使用变量名作为属性名。 const x 10; const y 20;// ES6之前 const obj1 { x: x, y: y };// ES6属性简写 const obj2 { x, y };注意:简写的对象方法不能用作构造…...

蓝桥杯每日一题2023.9.22
4960. 子串简写 - AcWing题库 题目描述 题目分析 原本为纯暴力但是发现会超时,可以加入前缀和,从前往后先记录一下每个位置c1出现的次数 再从前往后扫一遍,如果遇到c2就将答案加上此位置前的所有c1的个数(直接加上此位置的前缀…...

vscode左键无法跳转到定义的文件
之前用vscode的时候,明明是可以ctrl键鼠标左键跳转到定义文件的,突然之间就不行了,鼠标移到引入上根本都没有下划线,无法跳转 解决方法: 项目的根目录新建 jsconfig.json 文件,代码如下 {"compiler…...
c、c++排序的相关知识(归并排序、计数排序、稳定性等)
排序,是对给定的一组数,按照某种逻辑关系,进行位置上的移动。由于排序至少需要将所有数过一遍(正常情况下,非特殊数组),因此排序的时间复杂度一定不能小于O(N)。 归并排…...
oracle定时任务的使用
常见错误: PLS-00225: subprogram or cursor xxx reference is out of scope # job名字太长PLS-00201: identifier COUNT_JOB.SUBMIT must be declared # DBMS_JOB.SUBMIT是固定写法创建存储过程 -- 建表 CREATE TABLE TEST_A(TEST_ADD_DATA DATE); -- 存储过程 C…...

VSCode 配置 Lua 开发环境(清晰明了)
概述 由于 AutoJS 学得已经差不多了,基本都会了,现在开始向其他游戏脚本框架进发, Lua 语言很强大,就不多说, 按键精灵、触动精灵等等都是用该语言编程脚本的,由于按键精灵、触动精灵 和 AutoJS 类似,不是…...
JS合并2个远程pdf
要在HTML和JavaScript中读取远程PDF文件的矢量数据并合并两个PDF文件,您可以使用pdf-lib和Axios库。以下是使用pdf-lib和Axios在HTML和JavaScript中读取和合并远程PDF文件的步骤: 1. 引入 首先,确保您在HTML文件中引入了pdf-lib和Axios库。…...

TikTok的伦理挑战:虚拟世界与现实世界的交汇
在数字时代,社交媒体平台已经不再只是一个信息传播的工具,它已经深刻地改变了我们的社交行为、价值观和伦理观。 而在这一领域的佼佼者之一,TikTok,正面临着伦理挑战,这是虚拟世界与现实世界交汇的产物。 本文将深入…...
C# 获取磁盘空间大小的方法
方法一:利用System.IO.DriveInfo.GetDrives方法来获取 /// 获取指定驱动器的空间总大小(单位为B)////// 只需输入代表驱动器的字母即可 (大写)///public static long GetHardDiskSpace(string str_HardDiskName){long totalSize new long();…...

JVM机制理解与调优方案
作者:逍遥Sean 简介:一个主修Java的Web网站\游戏服务器后端开发者 主页:https://blog.csdn.net/Ureliable 觉得博主文章不错的话,可以三连支持一下~ 如有需要我的支持,请私信或评论留言! 前言 很多Java开发…...

Django的设计模式及模板层
Django的设计模式及模板层 设计模式MVC和MVT MVC 代表 Model-View-Controller(模型-视图-控制器)模式。 M 模型层(Model),主要用于对数据库层的封装 V 视图层(View),用于向用户展示结果 (WHAT HOW) C 控制(Controller,用于处理请求、获取数据、返回结果(重要) 作…...

写代码生成流程图
我们在写文档,博客的时候,一般都会使用markdown语法,最常见的就是一些github开源项目的README。有时候会去画一些流程图,例如使用process.on或者xmind等第三方网站,然后截图插入到文档中。 今天我们介绍一种使用代码直…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...

IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...