.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等第三方网站,然后截图插入到文档中。 今天我们介绍一种使用代码直…...
python reportlab生成pdf
这里自定义了pagetemplate,使用BaseDocTemplate,但我感觉一般使用SimpleDocTemplate就可以。 from reportlab.platypus import Frame from reportlab.lib.pagesizes import A4, landscapepadding dict(leftPadding72,rightPadding72,topPadding72,bott…...
第一次作业题解
第一次作业题解 P5717 【深基3.习8】三角形分类 思路 考的是if()的使用,还要给三条边判断大小 判断优先级: 三角形?直角、钝角、锐角等腰等边 判断按题给顺序来 代码 #include <stdio.h> int main() {int a 0, b 0, c 0, x 0, y 0, z 0…...
美篇作文网教学资源源码-自带作文数据
非常漂亮的UI设计和页面排版! 自适应手机pc端 页面内容均支持自定义 可以用来做网站矩阵,或者增强你其他网站板块,或者单独运营都可以。 可以通过广告方式变现,或者引流等等 友好的seo,更容易被浏览器收录 关注青狐…...
电脑软件:Duplicate Cleaner Pro 5.16 重复文件清理软件(附下载)
大家平时在使用电脑的时候,会经常从网上下载文件或者从其他电脑拷贝文件到自己的电脑上。久而久之就会在电脑中存放很多相同的文件,并且会越积越多,不仅占用很多磁盘空间,在文件管理上也非常混乱不方便。如何解决呢? …...
支持笔记本电脑直插直充,TOWE 65W智能快充PDU超级插座
电源插排在我们的生活中是必不可少的电器配件。今天,我们日常生活中所使用的电子设备越来越多,无论是手机、平板、笔记本电脑还是各种家用电器,都需要电源来驱动。虽然相对于其他电器来说,插排结构比较简单,但现代家庭…...
部署Kafka
kafka:kafka_2.13-3.5.1 NOTE: Your local environment must have Java 8 installed. Apache Kafka can be started using ZooKeeper or KRaft. To get started with either configuration follow one the sections below but not both. 1 Windows单机 1.1 Kafka w…...
Open3D 进阶(11)使用GMM-Tree算法对点云配准
GMM-Tree算法 一、算法原理1、主要函数2、参考文献二、代码实现三、结果展示1、点云初始位置2、配准后的位置四、测试数据本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、算法原理 1、...
算法刷题注意事项
目录 1、使用nextInt后再使用nextLine的注意事项2、判断x是否是素数3、注意字符串和数字的排序4、Arrays.asList不可修改元素 1、使用nextInt后再使用nextLine的注意事项 使用nextInt再使用nextLine会有问题,输入的回车会被nextLine给接受,可以将nextLi…...
搭建自己的pypi服务器
要搭建自己的 PyPI 服务器,您可以使用 warehouse 项目,它是 PyPI 的开源实现。下面是一些基本步骤: 准备环境: 安装 Python安装 PostgreSQL 数据库 克隆 warehouse 项目: git clone https://github.com/pypa/wareh…...
ndoe.js、npm相关笔记
1、npm 全局安装 npm config get prefix 获取 npm 全局安装路径如果全局插件不能正常使用,看环境变量是否已经配置。没有配置则把全局安装路径配置到环境变量的path中...
哪家公司因为做网站失败了/微信裂变营销软件
我们最后的结果还是很仓促,不是很完善,但是我们会在不断的进行中转载于:https://www.cnblogs.com/caicaihong/p/5601988.html...
linux把wordpress/免费网站自助建站系统
当系统卡顿、出现 Bug、电脑崩坏的时候,应该重启,重装,还是重买呢?相信大多数网友对重装并不陌生了,PE 系统、系统重置、在线重装降低了重装系统的门槛,「重装大法」虽然可以解决 90% 的问题,简…...
thinkphpcmf网站开发/seo的重要性
Android Studio Profiler 检查内存简单介绍 如何使用? 第一步:点击Profiler按钮 第二步:选择 第三步:选择Capture heap dump 并点击Record 解释相关按钮的功能 垃圾桶按钮:用于强制执行垃圾回收事件的按钮ÿ…...
四川泸州做网站的公司有哪些/数字营销策划
1. 加载DOM $(document).ready(function(){ //code }); //简写 $(function(){ }) 与window.onload的区别:jquery的ready函数在DOM树加载完后即可执行,而window.onload则必须等待网页中所有元素加载完后才执行。window.onload只能执行一次,而r…...
万链网站做的怎么样/电商平台运营方案
为什么80%的码农都做不了架构师?>>> 一、New / Delete原理 当我们在程序中写下 new 和 delete 时,我们实际上调用的是 C 语言内置的 new operator 和 delete operator。所谓语言内置就是说我们不能更改其含义,它的功能总是一致的…...
郑州做网站好/品牌宣传策划方案
1、在虚拟机上安装 linux 首先到官网上下载centos 点击找到get centos 或者 download等进入下载页面点击 DVD ISO 进入(isoredirect.centos.org/centos/7/isos/x86_64/CentOS-7-x86_64-DVD-1611.iso) 选择一个镜像文件下载,这里选择了163下载…...