- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
最近在看项目,看到别人使用Rougamo框架,好奇花了点时间仔细研究了,在这里记录一下.
首先,我们先了解什么是Aop? Aop 是指面向切面编程 (Aspect Oriented Programming),而所谓的切面,可以认为是具体拦截的某个业务点.
我们常用的aop框架是 AspectCore,他是属于动态代理,也就是发生在运行时期间对代码进行“修改”.
Rougamo、Fody 是属于静态编织,是指在编译阶段将代码修改或额外的功能直接嵌入到程序集中,这个过程发生在源代码被编译成可执行文件或库之前。这意味着,一旦编译完成,插入的代码就已经是程序集的一部分,无需在运行时再进行额外的操作.
。
Rougamo 是一个开源项目,github: https://github.com/inversionhourglass/Rougamo,他是通过Fody -> Mono.Cecil 的方式实现静态编织 实现Aop功能.
创建控制台程序,Nuget安装 Rougamo.Fody 。
[AttributeUsage(AttributeTargets.Method)] public class LoggingAttribute : MoAttribute { public override void OnEntry(MethodContext context) { Console.WriteLine("执行方法 {0}() 开始,参数:{1}.", context.Method.Name, JsonConvert.SerializeObject(context.Arguments)); } public override void OnException(MethodContext context) { Console.WriteLine("执行方法 {0}() 异常,{1}.", context.Method.Name, context.Exception.Message); } public override void OnExit(MethodContext context) { Console.WriteLine("执行方法 {0}() 结束.", context.Method.Name); } public override void OnSuccess(MethodContext context) { Console.WriteLine("执行方法 {0}() 成功.", context.Method.Name); } } internal class Program { static void Main(string[] args) { Add(1, 2); AddAsync(1, 2); Divide(1, 2); } [Logging] static int Add(int a, int b) => a + b; [Logging] static Task<int> AddAsync(int a, int b) => Task.FromResult(a + b); [Logging] static decimal Divide(decimal a, decimal b) => a / b; }
运行后会自动创建FodyWeavers.xsd 和 FodyWeavers.xml 。
<?xml version="1.0" encoding="utf-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> <xs:element name="Weavers"> <xs:complexType> <xs:all> <xs:element name="Rougamo" minOccurs="0" maxOccurs="1" type="xs:anyType" /> </xs:all> <xs:attribute name="VerifyAssembly" type="xs:boolean"> <xs:annotation> <xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> </xs:annotation> </xs:attribute> <xs:attribute name="VerifyIgnoreCodes" type="xs:string"> <xs:annotation> <xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> </xs:annotation> </xs:attribute> <xs:attribute name="GenerateXsd" type="xs:boolean"> <xs:annotation> <xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> </xs:annotation> </xs:attribute> </xs:complexType> </xs:element> </xs:schema>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> <Rougamo /> </Weavers>
下面是运行结果 。
这时候我们可以看到 增加了LoggingAttribute 特性的方法在运行前、运行成功、运行结束 执行了 OnEntry(MethodContext context) 、OnSuccess(MethodContext context)、OnExit(MethodContext context) 方法,这时我们打开ILSpy工具,看看实际运行的代码 。
internal class Program { private static void Main(string[] args) { Add(1, 2); AddAsync(1, 2); Divide(1m, 2m); } [DebuggerStepThrough] private static int Add(int a, int b) { LoggingAttribute loggingAttribute = new LoggingAttribute(); IMo[] mos = new IMo[1] { loggingAttribute }; MethodContext methodContext = new MethodContext(null, typeof(Program), MethodBase.GetMethodFromHandle((RuntimeMethodHandle)/*OpCode not supported: LdMemberToken*/, typeof(Program).TypeHandle), isAsync: false, isIterator: false, mosNonEntryFIFO: false, mos, new object[2] { a, b }); loggingAttribute.OnEntry(methodContext); int result = default(int); if (methodContext.ReturnValueReplaced) { result = (int)methodContext.ReturnValue; loggingAttribute.OnExit(methodContext); return result; } if (methodContext.RewriteArguments) { a = (int)methodContext.Arguments[0]; b = (int)methodContext.Arguments[1]; } bool flag = default(bool); do { try { while (true) { try { flag = false; result = $Rougamo_Add(a, b); } catch (Exception exception) { methodContext.Exception = exception; methodContext.Arguments[0] = a; methodContext.Arguments[1] = b; loggingAttribute.OnException(methodContext); if (methodContext.RetryCount > 0) { continue; } if (methodContext.ExceptionHandled) { result = (int)methodContext.ReturnValue; break; } throw; } break; } } finally { if (methodContext.HasException || methodContext.ExceptionHandled) { goto IL_0160; } methodContext.ReturnValue = result; methodContext.Arguments[0] = a; methodContext.Arguments[1] = b; loggingAttribute.OnSuccess(methodContext); if (methodContext.RetryCount <= 0) { if (methodContext.ReturnValueReplaced) { result = (int)methodContext.ReturnValue; } goto IL_0160; } flag = true; goto end_IL_00fc; IL_0160: loggingAttribute.OnExit(methodContext); end_IL_00fc:; } } while (flag); return result; } [DebuggerStepThrough] private static Task<int> AddAsync(int a, int b) { LoggingAttribute loggingAttribute = new LoggingAttribute(); IMo[] mos = new IMo[1] { loggingAttribute }; MethodContext methodContext = new MethodContext(null, typeof(Program), MethodBase.GetMethodFromHandle((RuntimeMethodHandle)/*OpCode not supported: LdMemberToken*/, typeof(Program).TypeHandle), isAsync: false, isIterator: false, mosNonEntryFIFO: false, mos, new object[2] { a, b }); loggingAttribute.OnEntry(methodContext); Task<int> result = default(Task<int>); if (methodContext.ReturnValueReplaced) { result = (Task<int>)methodContext.ReturnValue; loggingAttribute.OnExit(methodContext); return result; } if (methodContext.RewriteArguments) { a = (int)methodContext.Arguments[0]; b = (int)methodContext.Arguments[1]; } bool flag = default(bool); do { try { while (true) { try { flag = false; result = $Rougamo_AddAsync(a, b); } catch (Exception exception) { methodContext.Exception = exception; methodContext.Arguments[0] = a; methodContext.Arguments[1] = b; loggingAttribute.OnException(methodContext); if (methodContext.RetryCount > 0) { continue; } if (methodContext.ExceptionHandled) { result = (Task<int>)methodContext.ReturnValue; break; } throw; } break; } } finally { if (methodContext.HasException || methodContext.ExceptionHandled) { goto IL_015b; } methodContext.ReturnValue = result; methodContext.Arguments[0] = a; methodContext.Arguments[1] = b; loggingAttribute.OnSuccess(methodContext); if (methodContext.RetryCount <= 0) { if (methodContext.ReturnValueReplaced) { result = (Task<int>)methodContext.ReturnValue; } goto IL_015b; } flag = true; goto end_IL_00fc; IL_015b: loggingAttribute.OnExit(methodContext); end_IL_00fc:; } } while (flag); return result; } [DebuggerStepThrough] private static decimal Divide(decimal a, decimal b) { LoggingAttribute loggingAttribute = new LoggingAttribute(); IMo[] mos = new IMo[1] { loggingAttribute }; MethodContext methodContext = new MethodContext(null, typeof(Program), MethodBase.GetMethodFromHandle((RuntimeMethodHandle)/*OpCode not supported: LdMemberToken*/, typeof(Program).TypeHandle), isAsync: false, isIterator: false, mosNonEntryFIFO: false, mos, new object[2] { a, b }); loggingAttribute.OnEntry(methodContext); decimal result = default(decimal); if (methodContext.ReturnValueReplaced) { result = (decimal)methodContext.ReturnValue; loggingAttribute.OnExit(methodContext); return result; } if (methodContext.RewriteArguments) { a = (decimal)methodContext.Arguments[0]; b = (decimal)methodContext.Arguments[1]; } bool flag = default(bool); do { try { while (true) { try { flag = false; result = $Rougamo_Divide(a, b); } catch (Exception exception) { methodContext.Exception = exception; methodContext.Arguments[0] = a; methodContext.Arguments[1] = b; loggingAttribute.OnException(methodContext); if (methodContext.RetryCount > 0) { continue; } if (methodContext.ExceptionHandled) { result = (decimal)methodContext.ReturnValue; break; } throw; } break; } } finally { if (methodContext.HasException || methodContext.ExceptionHandled) { goto IL_0160; } methodContext.ReturnValue = result; methodContext.Arguments[0] = a; methodContext.Arguments[1] = b; loggingAttribute.OnSuccess(methodContext); if (methodContext.RetryCount <= 0) { if (methodContext.ReturnValueReplaced) { result = (decimal)methodContext.ReturnValue; } goto IL_0160; } flag = true; goto end_IL_00fc; IL_0160: loggingAttribute.OnExit(methodContext); end_IL_00fc:; } } while (flag); return result; } [Logging] private static int $Rougamo_Add(int a, int b) { return a + b; } [Logging] private static Task<int> $Rougamo_AddAsync(int a, int b) { return Task.FromResult(a + b); } [Logging] private static decimal $Rougamo_Divide(decimal a, decimal b) { return a / b; } }
从实际运行的代码我们可以看到,原先Add(int a, int b)方法中的执行内容被移动到 $Rougamo_Add方法中,而Add(int a, int b)方法先是new LoggingAttribute() 和 new Rougamo.Context.MethodContext() -> 执行了 loggingAttribute.OnEntry(methodContext); -> 在do{}while(bool) 执行了$Rougamo_Add(a, b); -> 在 exception 中执行了loggingAttribute.OnException(methodContext); -> 在 finally中执行了 loggingAttribute.OnSuccess(methodContext); 和 loggingAttribute.OnExit(methodContext),
注:do{}while(bool) 执行了$Rougamo_Add(a, b); 是因为 Rougamo 可以实现方法执行失败重试功能 。
至此我们明白了 Rougamo 实现 Aop功能是通过编译时修改IL代码,往代码增加对应的生命周期代码。那他为什么可以做到呢?其实是借用了Fody -> Mono.Cecil 的方式.
代码如下:https://gitee.com/Karl_Albright/csharp-demo/tree/master/FodyDemo/RougamoDemo 。
。
Fody 是一个开源项目,github: https://github.com/Fody/Fody,相关教程文档在 https://github.com/Fody/Home/tree/master/pages 。
创建类库,选择netstandard2.0,命名为HelloWorld,Nuget安装 Fody 和 FodyPackaging 。
注:必须创建 netstandard2.0,因为FodyPackaging的目标是netstandard2.0, 。
在HelloWorld项目中,我们只放 HWAttribute类,继承于 Attribute。代码如下 。
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property)] public class HWAttribute : Attribute { }
。
再次创建类库,选择netstandard2.0,命名为HelloWorld.Fody,Nuget安装 FodyHelpers,引用HelloWorld类库 。
在HelloWorld.Fody项目中,我们只放ModuleWeaver类(类名是固定的,详情见Fody文档),继承于 BaseModuleWeaver。代码如下 。
using Fody; using Mono.Cecil; using Mono.Cecil.Cil; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace HelloWorld.Fody { public partial class ModuleWeaver : BaseModuleWeaver { public override void Execute() { foreach (var type in ModuleDefinition.Types) { foreach (var method in type.Methods) { var customerAttribute = method.CustomAttributes.FirstOrDefault(x => x.AttributeType.Name == nameof(HWAttribute)); if (customerAttribute != null) { ProcessMethod(method); } } } } public override IEnumerable<string> GetAssembliesForScanning() { yield return "mscorlib"; yield return "System"; } private MethodInfo _writeLineMethod => typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }); private void ProcessMethod(MethodDefinition method) { // 获取当前方法体中的第一个IL指令 var processor = method.Body.GetILProcessor(); var current = method.Body.Instructions.First(); // 插入一个 Nop 指令,表示什么都不做 var first = Instruction.Create(OpCodes.Nop); processor.InsertBefore(current, first); current = first; // 构造 Console.WriteLine("Hello World") foreach (var instruction in GetInstructions(method)) { processor.InsertAfter(current, instruction); current = instruction; } } private IEnumerable<Instruction> GetInstructions(MethodDefinition method) { yield return Instruction.Create(OpCodes.Nop); yield return Instruction.Create(OpCodes.Ldstr, "Hello World."); yield return Instruction.Create(OpCodes.Call, ModuleDefinition.ImportReference(_writeLineMethod)); } } }
在代码中,我们遍历了所有类型的所有方法,如果方法标注了 HWAttribute特性,则增加 Console.WriteLine("Hello World."); 代码.
。
创建控制台应用程序,命名为HelloWorldFodyDemo,添加 HelloWorld 和 HelloWorld.Fody 项目引用,并且手动增加 WeaverFiles标签,目标是HelloWorld.Fody.dll 。
在控制台中,我们需要一个方法,方法上有 HWAttribute 特性就可以了,代码如下 。
internal class Program { static void Main(string[] args) { Echo(); Console.ReadKey(); } [HW] public static void Echo() { Console.WriteLine("Hello Fody."); } }
在控制台项目中,我们还需要 FodyWeavers.xml 和 FodyWeavers.xsd 文件,(我也是从上面Rougamo项目中复制的),内容如下 。
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> <HelloWorld /> </Weavers>
<?xml version="1.0" encoding="utf-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="Weavers"> <xs:complexType> <xs:all> <xs:element name="HelloWorld" minOccurs="0" maxOccurs="1" type="xs:anyType" /> </xs:all> <xs:attribute name="VerifyAssembly" type="xs:boolean"> <xs:annotation> <xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> </xs:annotation> </xs:attribute> <xs:attribute name="VerifyIgnoreCodes" type="xs:string"> <xs:annotation> <xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> </xs:annotation> </xs:attribute> <xs:attribute name="GenerateXsd" type="xs:boolean"> <xs:annotation> <xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> </xs:annotation> </xs:attribute> </xs:complexType> </xs:element> </xs:schema>
目前,文件结构如下 。
FodyDemo
|--- HelloWorld
|--- HWAttribute.cs
|--- HelloWorld.csproj
|--- HelloWorld.Fody
|--- HelloWorld.Fody.csproj
|--- ModuleWeaver.cs
|--- HelloWorldFodyDemo
|--- FodyWeavers.xml
|--- FodyWeavers.xsd
|--- HelloWorldFodyDemo.csproj
|--- Program.cs
代码如下:https://gitee.com/Karl_Albright/csharp-demo/tree/master/FodyDemo 。
最后运行结果如下,很明显,HWAttribute生效了,我们成功的在Echo()方法前打印了Hello World.
我们再次打开ILSpy工具,得到的结果如图,代码增加了Console.WriteLine("Hello World.");行代码 。
4. Fody 有很多其他的“插件”,大家可以多试试 。
AutoProperties.Fody: 这个外接程序为您提供了对自动属性的扩展控制,比如直接访问backing字段或拦截getter和setter.
PropertyChanged.Fody: 将属性通知添加到实现INotifyPropertyChanged的所有类.
InlineIL.Fody: 在编译时注入任意IL代码.
MethodDecorator.Fody:通过IL重写编译时间装饰器模式.
NullGuard.Fody: 将空参数检查添加到程序集.
ToString.Fody: 给属性生成ToString()方法 。
Rougamo.Fody: 在编译时生效的AOP组件,类似于PostSharp.
。
最后此篇关于Rougamo、Fody实现静态Aop的文章就讲到这里了,如果你想了解更多关于Rougamo、Fody实现静态Aop的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
关闭。这个问题是opinion-based .它目前不接受答案。 想要改进这个问题? 更新问题,以便 editing this post 可以用事实和引用来回答它. 关闭 5 年前。 Improve
在 C# 静态方法中是否有一种方法可以引用定义该方法的类型? 在实例方法中,您可以通过以下方式确定类型: public void Foo() { Type type = this.GetTyp
WPF:静态、动态资源以及资源词典 静态资源与动态资源 我们常常会使用样式或者控件模板放在Window.Resources中,比如这样: 静态资源与动态资源使用如下: <Window
任何人都知道如何在共享/静态函数中动态加载控件?该函数本身位于 mustinherit/abstract 类中。 (这是 VB 中的 ASP.NET 项目)我想做这样的事情: VB: Publ
在我看来,静态/强类型编程语言最宝贵的一点是它有助于重构:如果/当您更改任何 API,那么编译器会告诉您该更改破坏了什么。 我可以想象用运行时/弱类型语言编写代码......但我无法想象没有编译器的帮
正如我的名字所暗示的,我是一名 .NET 开发人员,但我对 Java 的兴趣越来越大,并且我有兴趣学习更多其他语言,因为这有助于我学习更多关于编程的知识。 无论如何,我的问题是:不带参数/不使用状态的
我在java中使用WireMock来 stub POST请求。该请求返回一个存储在我本地的 json 正文文件。 stub 看起来像这样: wireMockServer.stubFor(get(url
Python 是否有类构造函数的机制,即每当首次引用类时(而不是创建该对象的实例时)调用的函数?我知道其他一些语言中也存在这种情况,但我还没有在 Python 中遇到过。 基本上,我想初始化该函数中的
Python 是否有类构造函数的机制,即每当首次引用类时(而不是创建该对象的实例时)调用的函数?我知道其他一些语言中也存在这种情况,但我还没有在 Python 中遇到过。 基本上,我想初始化该函数中的
这个问题已经有答案了: What is the difference between dynamic and static polymorphism in Java? (14 个回答) 已关闭 4 年
这个问题已经有答案了: 已关闭10 年前。 Possible Duplicate: Static initializer in Java 我想知道这个静态的东西(抱歉,这是我第一次遇到这个)对一个类有
如果c++应用程序是按以下方式组织的 //file1.cpp static Y sgObj = X::getInitObject(0); //declared in file scope //fil
我有一个抽象类(AvergedDataRecord),我需要进一步抽象(DataRecord),这样我就可以将它扩展到原始类和一个新的具体类(SummedDataRecord),并且我在获取某些方法时
我正在尝试制作一个字符串枚举。这是我到目前为止所得到的, private class TypedEnum : IEnumerable { public IEnumerator GetEnume
我选修了一门名为“安全代码”的类(class),在下一个作业中,我们应该对一些 C 文件和 JavaEE Web 项目进行静态/动态分析。 我检查了“源监视器”并在 C 文件上运行它,但是(除非我不知
我有两个类,一个是登录类,一个是用户类。在 loggedIn 类中,我想显示我在用户登录时所做的共享首选项。 loginPrefs = getSharedPreferences("loginprefe
我在同一个 Activity 中有两个静态 fragment ,在“fragmentA”中我有一个自定义列表,当一个项目被点击时必须在“fragmentB”中出现一个细节,细节只在我改变屏幕方向时出现
在 Java 中是未修改方法变量,缺少final,每次都重新初始化限定符 静态方法 实例方法 如果 1. 或 2.(或两者)的答案是 final 限定符允许 Java 执行优化并存储方法变量只有一次?
我有两个类相互交互。第一个是中心的,如下: public class Datenbank { double winkelPanel = 0; double groessePanel = 0; doub
我有一个 mysql 数据库,它连接基于 Web 的 php 应用程序和 FoxPro 应用程序(是的,foxpro)。在之前的“开发人员”被解雇后开始处理这个问题。 无论如何,我熟悉 AES_Enc
我是一名优秀的程序员,十分优秀!