上接:思想無語言邊界:以 cglib 介紹 AOP 在 java 的一個實現方式java
做爲第四篇,咱們回顧一下 csharp 裏面比較常見動態編織實現方式emitgit
內容安排以下:github
emit 是相似 java 中ASM地位的一個底層功能實現,編程
不過不是轉化java字節碼,而是生成dotnet 的 IL代碼,api
生成的IL代碼將由內置的JIT編譯器直接編譯到內存中。框架
官方的介紹文檔async
emit 對你們來講都是很熟悉的api了,動態作什麼事基本都會想到它。編程語言
咱們是可使用emit 作到上篇java 的 cglib 如出一轍的動態編織的AOP效果,因此語言真的只是工具,怎麼玩取決於玩工具的人,demo 以下。ide
代碼工具
public abstract class MethodInterceptor { public abstract object Invoke(object instance, MethodInfo methodInfo, object[] parameters, object returnValue); } public static class ProxyGenerator { private static ModuleBuilder moduleBuilder; private static MethodInfo getMethodMethod = typeof(MethodBase).GetMethod("GetMethodFromHandle", new[] { typeof(RuntimeMethodHandle) }); private static MethodInfo invoke = typeof(MethodInterceptor).GetMethod("Invoke"); static ProxyGenerator() { var asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("EmitAopDemoTest"), AssemblyBuilderAccess.RunAndCollect); moduleBuilder = asmBuilder.DefineDynamicModule("Proxy"); } public static T Generate<T>(Type methodInterceptorType) { var proxyType = GenerateProxyType(typeof(T), methodInterceptorType); return (T)Activator.CreateInstance(proxyType); } public static Type GenerateProxyType(Type type, Type methodInterceptorType) { var typeBuilder = moduleBuilder.DefineType($"{type.Name}Proxy", TypeAttributes.Class | TypeAttributes.Public, type); foreach (var m in type.GetTypeInfo().DeclaredMethods) { var ps = m.GetParameters().Select(i => i.ParameterType).ToArray(); var newM = typeBuilder.DefineMethod(m.Name, MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, m.CallingConvention, m.ReturnType, ps); CreateProxyMethod(methodInterceptorType, m, ps, newM); typeBuilder.DefineMethodOverride(newM, m); } return typeBuilder.CreateType(); } private static void CreateProxyMethod(Type methodInterceptorType, MethodInfo m, Type[] ps, MethodBuilder newM) { var il = newM.GetILGenerator(); var argsLocal = il.DeclareLocal(typeof(object[])); var returnLocal = il.DeclareLocal(typeof(object)); // 初始化參數集合 il.Emit(OpCodes.Ldc_I4, ps.Length); il.Emit(OpCodes.Newarr, typeof(object)); for (var i = 0; i < ps.Length; i++) { il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldc_I4, i); il.Emit(OpCodes.Ldarg, i + 1); il.Emit(OpCodes.Box, ps[i]); il.Emit(OpCodes.Stelem_Ref); } il.Emit(OpCodes.Stloc, argsLocal); // 調用被代理方法 il.Emit(OpCodes.Ldarg, 0); // load this for (var i = 0; i < ps.Length; i++) { il.Emit(OpCodes.Ldarg, i + 1); } il.Emit(OpCodes.Call, m); il.Emit(OpCodes.Box, newM.ReturnType); il.Emit(OpCodes.Stloc, returnLocal); //調用方法後攔截器 il.Emit(OpCodes.Newobj, methodInterceptorType.GetConstructors().First()); il.Emit(OpCodes.Ldarg, 0); // load this //加載方法信息參數 il.Emit(OpCodes.Ldtoken, m); il.Emit(OpCodes.Call, getMethodMethod); il.Emit(OpCodes.Castclass, typeof(MethodInfo)); il.Emit(OpCodes.Ldloc, argsLocal); il.Emit(OpCodes.Ldloc, returnLocal); il.Emit(OpCodes.Callvirt, invoke); il.Emit(OpCodes.Stloc, returnLocal); // return il.Emit(OpCodes.Ldloc, returnLocal); il.Emit(OpCodes.Unbox_Any, newM.ReturnType); il.Emit(OpCodes.Ret); } }
internal class Program { private static void Main(string[] args) { RealClass proxy = ProxyGenerator.Generate<RealClass>(typeof(AddOneInterceptor)); var i = 5; var j = 10; Console.WriteLine($"{i} + {j} = {(i + j)}, but proxy is {proxy.Add(i, j)}"); } }
結果:
5 + 10 = 15, but proxy is 16
至此,
本系列已經介紹完了全部的aop實現方式,
以csharp 平臺重點舉例介紹了AOP 靜態編織和動態編織 的方法。
並以 java cglib 表達了思想無編程語言邊界。
最後呢,介紹一下本身正在作的項目 Norns.Urd
Github: https://github.com/fs7744/Norns.Urd
Norns.Urd 是一個基於emit實現動態代理的輕量級AOP框架.
版本基於 netstandard2.0. 因此哪些.net 版本能用你懂的。
完成這個框架的目的主要出自於我的如下意願:
但願看過你們作碼農作的開心。