日志系统实战(二)-AOP动态获取运行时数据
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了日志系统实战(二)-AOP动态获取运行时数据,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含5310字,纯文字阅读大概需要8分钟。
内容图文
![日志系统实战(二)-AOP动态获取运行时数据](/upload/InfoBanner/zyjiaocheng/1333/9c1a07e768c44d2fb197dcc1d1c2c662.jpg)
介绍
这篇距上一篇已经拖3个月之久了,批评自己下。
通过前面一篇介绍。我们通过mono反射代码,可以拿出编译好的静态数据、例如方法参数信息之类的。但实际情况是:我更需要运行时的数据,就是用户输入等外界的动态数据。
既然是动态的,那就是未知的。我们怎么通过提前注入的代码获取呢?
其实这是一个思路的问题,下面我们具体细看下。
实现
一 普通写法
public static string GetPoint(int x, int y) { var value=x; }
哇 好简单啊。其实动态获取和我们普通这样写代码是一样的,我们把要注入的代码,生成一个接收的变量就可以了。 就像上面value 一样接收,然后传递给我们自己函数就好了。
二 注入定义
public class WeaveService : Attribute { } public class WeaveAction : Attribute { } public class Log : WeaveAction { public static void OnActionBefore(MethodBase mbBase, object[] args) { for (int i = 0; i < args.Length; i++) { Console.WriteLine(string.Format("{0}方法,第{1}参数是:{2}",mbBase.Name,i, args[i])); } } }
WeaveService WeaveAction 2个Attribute是注入的标记,方便我们在注入查找快速定位。
OnActionBefore是我们的接收函数,arg就是函数运行时的参数。
三 Weave函数
这块代码在上一篇已经有详细注释了,这里不多描述。
public static void Weave(string[] assemblyPath) { foreach (var item in assemblyPath) { var assembly = AssemblyDefinition.ReadAssembly(item); var types = assembly.MainModule.Types.Where(n => n.CustomAttributes.Any(y => y.AttributeType.Resolve().Name == "WeaveService")); foreach (var type in types) { foreach (var method in type.Methods) { var attrs = method.CustomAttributes.Where(y => y.AttributeType.Resolve().BaseType.Name == "WeaveAction"); foreach (var attr in attrs) { var resolve = attr.AttributeType.Resolve(); var ilProcessor = method.Body.GetILProcessor(); var firstInstruction = ilProcessor.Body.Instructions.First(); var onActionBefore = resolve.GetMethods().Single(n => n.Name == "OnActionBefore"); var mfReference = assembly.MainModule.Import(typeof(System.Reflection.MethodBase).GetMethod("GetCurrentMethod")); ilProcessor.InsertBefore(firstInstruction, ilProcessor.Create(OpCodes.Call, mfReference)); MakeArrayOfArguments(method, firstInstruction, ilProcessor, 0, method.Parameters.Count, assembly); ilProcessor.InsertBefore(firstInstruction, ilProcessor.Create(OpCodes.Call, onActionBefore)); } } } if (types.Any()) { assembly.Write(item); } } }
三 MakeArrayOfArguments 函数。
动态获取函数参数的函数,代码有详细注释。
1 /// <summary> 2 /// 构建函数参数 3 /// </summary> 4 /// <param name="method"> 要注入的方法 </param> 5 /// <param name="firstInstruction"> 函数体内第一行指令认 IL_0000: nop </param> 6 /// <param name="writer"> mono IL处理容器 </param> 7 /// <param name="firstArgument"> 默认第0个参数开始 </param> 8 /// <param name="argumentCount"> 函数参数的数量,静态数据可以拿到 </param> 9 /// <param name="assembly"> 要注入的程序集 </param> 10 public static void MakeArrayOfArguments(MethodDefinition method, Instruction firstInstruction, ILProcessor writer, int firstArgument, 11int argumentCount, AssemblyDefinition assembly) 12 { 13//实例函数第一个参数值为this(当前实例对象),所以要从1开始。14int thisShift = method.IsStatic ? 0 : 1; 1516if (argumentCount > 0) 17 { 18//我们先创建个和原函数参数,等长的空数组。19 writer.InsertBefore(firstInstruction, writer.Create(OpCodes.Ldc_I4, argumentCount - firstArgument)); 20//然后实例object数组,赋值给我们创建的数组21 writer.InsertBefore(firstInstruction, writer.Create(OpCodes.Newarr, 22 assembly.MainModule.Import(typeof(object)))); 2324//c#代码描述 25//object[] arr=new object[argumentCount - firstArgument] 26for (int i = firstArgument; i < argumentCount; i++) //遍历参数27 { 28var parameter = method.Parameters[i]; 2930//在堆栈上复制一个值31 writer.InsertBefore(firstInstruction, writer.Create(OpCodes.Dup)); 32//将常量 i - firstArgument 进行压栈,数组[i - firstArgument] 这个东东。33 writer.InsertBefore(firstInstruction, writer.Create(OpCodes.Ldc_I4, i - firstArgument)); 34//将第i + thisShift个参数 压栈。 35 writer.InsertBefore(firstInstruction, writer.Create(OpCodes.Ldarg, (short)(i + thisShift))); 36//装箱成object37 ToObject(assembly, firstInstruction, parameter.ParameterType, writer); 38//压栈给数组 arr[i]赋值39 writer.InsertBefore(firstInstruction, writer.Create(OpCodes.Stelem_Ref)); 4041//c#代码描述 42// arr[i]=value;43 } 44 } 45else46 { 47 writer.InsertBefore(firstInstruction, writer.Create(OpCodes.Ldnull)); 48 } 49 } 50publicstaticvoid ToObject(AssemblyDefinition assembly, Instruction firstInstruction, TypeReference originalType, ILProcessor writer) 51 { 52if (originalType.IsValueType) 53 { 54//普通值类型进行装箱操作55 writer.InsertBefore(firstInstruction, writer.Create(OpCodes.Box, originalType)); 56 } 57else58 { 59if (originalType.IsGenericParameter) 60 { 61//集合装箱62 writer.InsertBefore(firstInstruction, writer.Create(OpCodes.Box, assembly.MainModule.Import(originalType))); 63 } 6465 } 66 }
介绍下mono InsertBefore这个函数。 这个函数是在某个指令之前插入指令。来张图
通过图我们看出,第一行指令是IL_0000: nop 。 第一行追加了 ldc.i4 2 指令,第二行我们还是nop 之前追加。 自上而下
四 业务编写
我定义个要注入的用户类,然后标记下。
[WeaveService] public static class UserManager { [Log] public static string GetUserName(int userId, string memberid) { return "成功"; } [Log] public static string GetPoint(int x, int y) { var sum = x + y; return "用户积分: " + sum; } }
我们平常的业务写法,不需要多余操作。
public static void Main(string[] args) { UserManager.GetUserName(1,"v123465"); UserManager.GetPoint(2, 3); Console.ReadLine(); }
五 注入调用
我把业务类编译输入到D盘,用我们前面的函数Weave进行注入。
CodeInject.Weave(newstring[] { @"D:\test\Test.exe" });
运行结果如下
反编译后的c#
总结
通过静态注入,我们能更好的从用途上去了解IL语言。
拿到动态数据仅仅抛砖引玉。 利用Mono我们可以写自己的AOP静态组件。
参考资源
1:postsharp源码
2:http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes_fields(v=vs.110).aspx
作者:蘑菇先生
出处:http://www.cnblogs.com/mushroom/p/4124878.html
转载请保留作者和原文地址。
原文:http://www.cnblogs.com/mushroom/p/4124878.html
内容总结
以上是互联网集市为您收集整理的日志系统实战(二)-AOP动态获取运行时数据全部内容,希望文章能够帮你解决日志系统实战(二)-AOP动态获取运行时数据所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。