常见的 emit 实现 AOP demo

用户头像
八苦-瞿昙
关注
发布于: 2020 年 07 月 20 日
常见的emit实现AOP demo

-0.1 系列目录

  1. 无价值人生记录.0

  2. AOP 有几种实现方式?

  3. 用 Roslyn 做个 JIT 的 AOP

  4. 基于 Source Generators 做个 AOP 静态编织小实验

  5. 思想无语言边界:以 cglib 介绍 AOP 在 java 的一个实现方式

  6. 常见的emit实现AOP demo

0. 前言

副标题:无价值人生记录.0:浪费 1000% 时间去做一个用来节省 1% 时间的“轮子玩具”(下:AOP实践4 emit)

上接:思想无语言边界:以 cglib 介绍 AOP 在 java 的一个实现方式https://xie.infoq.cn/article/9c5886077f12f2d1d6ce7c4f0

作为第四篇,我们回顾一下 csharp 里面比较常见动态编织实现方式emit

内容安排如下:

  • emit aop demo



1. emit aop demo

1.1 emit 介绍

emit 是类似 java 中ASM地位的一个底层功能实现,

不过不是转化java字节码,而是生成dotnet 的 IL代码,

生成的IL代码将由内置的JIT编译器直接编译到内存中。

官方的介绍文档

emit 对大家来说都是很熟悉的api了,动态做什么事基本都会想到它。



我们是可以使用emit 做到上篇java 的 cglib 一模一样的动态编织的AOP效果,所以语言真的只是工具,怎么玩取决于玩工具的人,demo 如下。

1.2 demo

代码

1.2.1 ProxyGenerator 简单实现

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);
}
}



1.2.2 Test

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



2. 完结

至此,

本系列已经介绍完了所有的aop实现方式,

以csharp 平台重点举例介绍了AOP 静态编织和动态编织 的方法。

并以 java cglib 表达了思想无编程语言边界。

希望看过大家做码农做的开心。

发布于: 2020 年 07 月 20 日 阅读数: 102
用户头像

八苦-瞿昙

关注

一个假和尚,不懂人情世故。 2018.11.23 加入

会点点技术,能写些代码,只爱静静。 g hub: https://github.com/fs7744 黑历史:https://www.cnblogs.com/fs7744

评论

发布
暂无评论
常见的emit实现AOP demo