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

.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

  1. 动态代理实现方式:在Java Spring Boot中,基于代理的AOP主要使用JDK动态代理和CGLIB代理来实现。而在C#中,使用IL生成器(ILGenerator)直接操作IL指令来生成和修改类型的字节码,实现动态代理。这种方式相对于代理类的生成更加底层,需要对CLR(Common Language Runtime)和IL指令有一定的了解。

  2. IL的语法和特性:IL是.NET平台的中间语言,类似于汇编语言,但具有一些.NET特定的语法和特性。IL指令用于描述类型、方法、属性等的结构和操作,需要了解这些指令的使用规则和约束。相比之下,Java字节码(Java bytecode)是Java虚拟机(JVM)上的中间语言,其语法和特性与IL有所不同。

  3. 第三方库和框架:在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

首先&#xff0c;我们需要定义一个接口&#xff0c;代表我们要代理的目标对象的功能&#xff1a; // 日志记录器接口 public interface ILogger {/// <summary>/// 记录日志/// </summary>/// <param name"message">日志消息</param>void L…...

美容店预约小程序搭建流程

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

ppt 作图 如何生成eps格式

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

渗透测试中的前端调试(上)

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

跨境电商引流之Reddit营销,入门保姆级攻略

在当今竞争激烈的在线市场中&#xff0c;企业不断寻求新的方法来加强其数字营销工作。Reddit 是最受欢迎的社交媒体平台之一&#xff0c;为企业提供了巨大的潜力&#xff0c;可以通过引人入胜且相关的内容来接触目标受众。然而&#xff0c;将 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时报错了&#xff1a; conan install boost/1.81.0 -pr:h aarch64-linux-gnu.jinja -pr:b default --build boost输出如下&#xff1a; Configuration (profile_host): [settings] archarmv8 arch_b…...

BFS专题7 多终点迷宫问题

题目&#xff1a; 样例&#xff1a; 输入 3 3 0 0 0 1 0 0 0 1 0 输出 0 1 2 -1 2 3 -1 -1 4 思路&#xff1a; 单纯的 BFS 迷宫问题&#xff0c;只是标记一下每个点的 step&#xff0c;注意初始化答案数组都为 -1. 代码详解如下&#xff1a; #include <iostream> #…...

ES6中对象新增了哪些扩展?

一、属性的简写 当对象字面量的属性名与变量名相同时&#xff0c;可以省略属性名&#xff0c;直接使用变量名作为属性名。 const x 10; const y 20;// ES6之前 const obj1 { x: x, y: y };// ES6属性简写 const obj2 { x, y };注意&#xff1a;简写的对象方法不能用作构造…...

蓝桥杯每日一题2023.9.22

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

vscode左键无法跳转到定义的文件

之前用vscode的时候&#xff0c;明明是可以ctrl键鼠标左键跳转到定义文件的&#xff0c;突然之间就不行了&#xff0c;鼠标移到引入上根本都没有下划线&#xff0c;无法跳转 解决方法&#xff1a; 项目的根目录新建 jsconfig.json 文件&#xff0c;代码如下 {"compiler…...

c、c++排序的相关知识(归并排序、计数排序、稳定性等)

排序&#xff0c;是对给定的一组数&#xff0c;按照某种逻辑关系&#xff0c;进行位置上的移动。由于排序至少需要将所有数过一遍&#xff08;正常情况下&#xff0c;非特殊数组&#xff09;&#xff0c;因此排序的时间复杂度一定不能小于O&#xff08;N&#xff09;。 归并排…...

oracle定时任务的使用

常见错误&#xff1a; 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 学得已经差不多了&#xff0c;基本都会了&#xff0c;现在开始向其他游戏脚本框架进发&#xff0c; Lua 语言很强大&#xff0c;就不多说&#xff0c; 按键精灵、触动精灵等等都是用该语言编程脚本的&#xff0c;由于按键精灵、触动精灵 和 AutoJS 类似,不是…...

JS合并2个远程pdf

要在HTML和JavaScript中读取远程PDF文件的矢量数据并合并两个PDF文件&#xff0c;您可以使用pdf-lib和Axios库。以下是使用pdf-lib和Axios在HTML和JavaScript中读取和合并远程PDF文件的步骤&#xff1a; 1. 引入 首先&#xff0c;确保您在HTML文件中引入了pdf-lib和Axios库。…...

TikTok的伦理挑战:虚拟世界与现实世界的交汇

在数字时代&#xff0c;社交媒体平台已经不再只是一个信息传播的工具&#xff0c;它已经深刻地改变了我们的社交行为、价值观和伦理观。 而在这一领域的佼佼者之一&#xff0c;TikTok&#xff0c;正面临着伦理挑战&#xff0c;这是虚拟世界与现实世界交汇的产物。 本文将深入…...

C# 获取磁盘空间大小的方法

方法一&#xff1a;利用System.IO.DriveInfo.GetDrives方法来获取 /// 获取指定驱动器的空间总大小(单位为B)////// 只需输入代表驱动器的字母即可 &#xff08;大写&#xff09;///public static long GetHardDiskSpace(string str_HardDiskName){long totalSize new long();…...

JVM机制理解与调优方案

作者&#xff1a;逍遥Sean 简介&#xff1a;一个主修Java的Web网站\游戏服务器后端开发者 主页&#xff1a;https://blog.csdn.net/Ureliable 觉得博主文章不错的话&#xff0c;可以三连支持一下~ 如有需要我的支持&#xff0c;请私信或评论留言&#xff01; 前言 很多Java开发…...

Django的设计模式及模板层

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

写代码生成流程图

我们在写文档&#xff0c;博客的时候&#xff0c;一般都会使用markdown语法&#xff0c;最常见的就是一些github开源项目的README。有时候会去画一些流程图&#xff0c;例如使用process.on或者xmind等第三方网站&#xff0c;然后截图插入到文档中。 今天我们介绍一种使用代码直…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题&#xff1a;3564. 季节性销售分析 题目&#xff1a; 表&#xff1a;sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

Java数值运算常见陷阱与规避方法

整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

毫米波雷达基础理论(3D+4D)

3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...