.Net IL Emit 实现Aop面向切面之动态代理

AOP 实现方式就是增加一层代理层,这个代理可以用

以下技术实现:

.Net框架自带

  1. DispatchProxy
  2. Realproxy 
  3. DynamicObject

开源的框架有:

  1. ImpromptuInterface
  2.  PostSharp1.5
  3. Castle.DynamicProxy

实现代理的方式有两种一种是静态织入,一种是动态织入。

静态织入的话,相当于是在不影响业务的情况下,又对框架做了一层处理(PostSharp1.5)

而动态织入就是在应用运行的过程中动态插入到里面。

大部分都是采用动态织入实现的。

我这边要实现一个通用性,所以,不能直接用框架自带的,也不想用开源框架里的,感觉它们写的优点复杂。

所以,得使用反射来实现这样的一个功能。

具体作用,我是准备用在自己要写的RPC中,所以,这个主要针对接口部分做的动态代理

具体原理如下:

用RPC 的时候,需要公共的部分,这部分就是接口。

注:关于调试

只有 fw 框架才会能保存dll查看,如果用到.NetCore项目只能直接运行,所以,想要调试,还是要在fw框架下调试
生成的dll 可以用 反编译软件 dnSpy 进行获取。
也可以根据dnSpy这样的反编译软件获取IL代码进行编码

客户端会共享这个接口,如下。

    public interface IConsole
    {
        void Say();
    }

调用的方式会像这样

   var console = Activator.CreateInstance(dynamicType) as IConsole;

   console.Say();

具体代码如下:

static void Main(string[] args)
        {

            // 在看下面的代码之前,先明白这个依赖关系,即:
            // 方法->类型->模块->程序集


            //定义程序集的名称
            AssemblyName aName = new AssemblyName("DynamicAssemblyExample");

            // 创建一个程序集构建器
            // Framework应该这样:AppDomain.CurrentDomain.DefineDynamicAssembly
            AssemblyBuilder ab =
                AssemblyBuilder.DefineDynamicAssembly(
                    aName,
                    AssemblyBuilderAccess.RunAndSave);


            // 使用程序集构建器创建一个模块构建器
            ModuleBuilder mb = ab.DefineDynamicModule(aName.Name + ".dll");

            // 使用模块构建器创建一个类型构建器
            TypeBuilder tb = mb.DefineType("DynamicConsole");

            // 使类型实现IConsole接口
            tb.AddInterfaceImplementation(typeof(IConsole));

            var attrs = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.HideBySig | MethodAttributes.Final;

            // 使用类型构建器创建一个方法构建器
            MethodBuilder methodBuilder = tb.DefineMethod("Say", attrs, typeof(void), Type.EmptyTypes);

            // 通过方法构建器获取一个MSIL生成器
            var IL = methodBuilder.GetILGenerator();

            // 开始编写方法的执行逻辑

            // 将一个字符串压入栈顶
            IL.Emit(OpCodes.Ldstr, "I'm here.");

            // 调用Console.Writeline函数
            IL.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }));

            // 退出函数
            IL.Emit(OpCodes.Ret);

            //方法结束

            // 从类型构建器中创建出类型
            var dynamicType = tb.CreateType();

            ab.Save(aName.Name + ".dll");
            // 通过反射创建出动态类型的实例
            var console = Activator.CreateInstance(dynamicType) as IConsole;

            console.Say();


            Console.WriteLine("不错,完成了任务!");
            Console.ReadLine();
        }

然后,就会生成一个这样的类出来。

自己继承接口,并实现这个接口的类,然后,反射出来。

至此,核心原理已经完了。

接下来就是实际过程中的代理过程

会让一个代理对象成为这个接口类的字段。然后在每个方法的执行过程中都会调用这个对象执行,并返回相应的结果。

这个对象执行的过程就是调用方法到远程服务器并返回结果的过程。

接口如下:

    public interface IDemo
    {
        void Say();
        string Say(string msg);
        int Say(string a, int b, List<string> c, Kind kind);
        string Say(int b, List<string> c, Kind kind);

        List<string> Test { get; set; }
    }
    public enum Kind
    {
        a,
        b
    }

生成的对象如下(截图+生成的代码):

using System;
using System.Collections.Generic;
using ProxyDemo;

namespace InterfaceProxy.DnamicInterfaceProxy
{
	// Token: 0x02000002 RID: 2
	internal class IDemo : IDemo
	{
		// Token: 0x06000001 RID: 1 RVA: 0x00002050 File Offset: 0x00000250
		public IDemo(InvocationHandler A_1)
		{
		}

		// Token: 0x06000002 RID: 2 RVA: 0x00002064 File Offset: 0x00000264
		public void Say()
		{
			object[] args = new object[0];
			this._handler.InvokeMember(this, 100663304, "ProxyDemo.IDemo+Say", args);
		}

		// Token: 0x06000003 RID: 3 RVA: 0x00002098 File Offset: 0x00000298
		public string Say(string A_1)
		{
			object[] args = new object[]
			{
				A_1
			};
			return (string)this._handler.InvokeMember(this, 100663305, "ProxyDemo.IDemo+Say", args);
		}

		// Token: 0x06000004 RID: 4 RVA: 0x000020DC File Offset: 0x000002DC
		public int Say(string A_1, int A_2, List<string> A_3, Kind A_4)
		{
			object[] args = new object[]
			{
				A_1,
				A_2,
				A_3,
				A_4
			};
			return (int)this._handler.InvokeMember(this, 100663306, "ProxyDemo.IDemo+Say", args);
		}

		// Token: 0x06000005 RID: 5 RVA: 0x00002154 File Offset: 0x00000354
		public Kind Say(int A_1, List<string> A_2, Kind A_3)
		{
			object[] args = new object[]
			{
				A_1,
				A_2,
				A_3
			};
			return (Kind)this._handler.InvokeMember(this, 100663307, "ProxyDemo.IDemo+Say", args);
		}

		// Token: 0x17000001 RID: 1
		// (get) Token: 0x06000006 RID: 6 RVA: 0x000021BC File Offset: 0x000003BC
		// (set) Token: 0x06000007 RID: 7 RVA: 0x000021F4 File Offset: 0x000003F4
		public List<string> Test
		{
			get
			{
				object[] args = new object[0];
				return (List<string>)this._handler.InvokeMember(this, 100663308, "ProxyDemo.IDemo+get_Test", args);
			}
			set
			{
				object[] args = new object[]
				{
					value
				};
				this._handler.InvokeMember(this, 100663309, "ProxyDemo.IDemo+set_Test", args);
			}
		}

		// Token: 0x06000008 RID: 8 RVA: 0x00002234 File Offset: 0x00000434
		public List<string> get_Test()
		{
			object[] args = new object[0];
			return (List<string>)this._handler.InvokeMember(this, 100663308, "ProxyDemo.IDemo+get_Test", args);
		}

		// Token: 0x06000009 RID: 9 RVA: 0x0000226C File Offset: 0x0000046C
		public void set_Test(List<string> A_1)
		{
			object[] args = new object[]
			{
				A_1
			};
			this._handler.InvokeMember(this, 100663309, "ProxyDemo.IDemo+set_Test", args);
		}

		// Token: 0x04000001 RID: 1
		private InvocationHandler _handler = A_1;
	}
}

这样,就把对象设置成字段然后被调用起来。

这个对象的接口如下

    public interface InvocationHandler
    {
        object InvokeMember(object obj, int rid, string name, params object[] args);
    }

实现动态代理的代码如下

    public class DnamicInterfaceProxy
    {
        private static readonly string DllName;
        private static ModuleBuilder ModuleBuilder = null;
        private static AssemblyBuilder AssemblyBuilder = null;

        private static readonly ConcurrentDictionary<Type, Type> Maps = new ConcurrentDictionary<Type, Type>();
        static DnamicInterfaceProxy()
        {
            var assemblyName = new AssemblyName(nameof(DnamicInterfaceProxy));
            DllName = assemblyName.Name + ".dll";
            AssemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
            ModuleBuilder = AssemblyBuilder.DefineDynamicModule(DllName);
        }
        public static T Resolve<T>() where T : class
        {
            var hanlder = new DefaultInvocationHandler<T>();
            var interfaceType = typeof(T);
            if (interfaceType?.IsInterface != true)
            {
                throw new ArgumentException("interfaceType");
            }
            Maps.TryGetValue(interfaceType, out Type newType);
            if (newType == null)
            {
                newType = CreateType(interfaceType);
                Maps.TryAdd(interfaceType, newType);
            }
            return (T)Activator.CreateInstance(newType, hanlder);
        }
        public static void Save()
        {
            AssemblyBuilder.Save(DllName);
        }
        private static Type CreateType(Type interfaceType)
        {
            var tb = ModuleBuilder.DefineType(string.Format("{0}.{1}", typeof(DnamicInterfaceProxy).FullName, interfaceType.Name));
            tb.AddInterfaceImplementation(interfaceType);

            var fb = tb.DefineField("_handler", typeof(InvocationHandler), FieldAttributes.Private);

            CreateConstructor(tb, fb);
            CreateMethods(interfaceType, tb, fb);
            CreateProperties(interfaceType, tb, fb);

            return tb.CreateType();
        }
        private static void CreateConstructor(TypeBuilder tb, FieldBuilder fb)
        {
            var ctor = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(InvocationHandler) });
            var il = ctor.GetILGenerator();
             
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Stfld, fb);
            il.Emit(OpCodes.Ret);
        }

        private static void CreateMethods(Type interfaceType, TypeBuilder tb, FieldBuilder fb)
        {
            foreach (MethodInfo met in interfaceType.GetMethods())
            {
                CreateMethod(met, tb, fb);
            }
        }
        private static MethodBuilder CreateMethod(MethodInfo met, TypeBuilder tb, FieldBuilder fb)
        {
            var args = met.GetParameters();
            var mb = tb.DefineMethod(met.Name, MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.HideBySig,
                met.CallingConvention, met.ReturnType, args.Select(t => t.ParameterType).ToArray());
            var il = mb.GetILGenerator();
            il.DeclareLocal(typeof(object[]));

            if (met.ReturnType != typeof(void))
            {
                il.DeclareLocal(met.ReturnType);
            }

            il.Emit(OpCodes.Nop);
            il.Emit(OpCodes.Ldc_I4, args.Length);
            il.Emit(OpCodes.Newarr, typeof(object));
            il.Emit(OpCodes.Stloc_0);

            for (int i = 0; i < args.Length; i++)
            {
                il.Emit(OpCodes.Ldloc_0);
                il.Emit(OpCodes.Ldc_I4, i);
                il.Emit(OpCodes.Ldarg, 1 + i);
                var type = args[i].ParameterType;
                if (type.IsValueType)
                {
                    il.Emit(OpCodes.Box, type);
                }
                il.Emit(OpCodes.Stelem_Ref);
            }

            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldfld, fb);

            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldc_I4, met.MetadataToken);
            il.Emit(OpCodes.Ldstr, met.DeclaringType?.FullName + "+" + met.Name);
            il.Emit(OpCodes.Ldloc_0);
            il.Emit(OpCodes.Call, typeof(InvocationHandler).GetMethod(nameof(InvocationHandler.InvokeMember), BindingFlags.Instance | BindingFlags.Public));

            if (met.ReturnType == typeof(void))
            {
                il.Emit(OpCodes.Pop);
            }
            else
            {
                il.Emit(met.ReturnType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, met.ReturnType);
                il.Emit(OpCodes.Stloc_1);
                il.Emit(OpCodes.Ldloc_1);
            }
            il.Emit(OpCodes.Ret);

            return mb;
        }
        private static void CreateProperties(Type interfaceType, TypeBuilder tb, FieldBuilder fb)
        {
            foreach (var prop in interfaceType.GetProperties())
            {
                var pb = tb.DefineProperty(prop.Name, PropertyAttributes.SpecialName, prop.PropertyType, Type.EmptyTypes);
                var met = prop.GetGetMethod();
                if (met != null)
                {
                    var mb = CreateMethod(met, tb, fb);
                    pb.SetGetMethod(mb);
                }
                met = prop.GetSetMethod();
                if (met != null)
                {
                    var mb = CreateMethod(met, tb, fb);
                    pb.SetSetMethod(mb);
                }
            }
        }
    }

具体的实用代码如下

            var demo = DnamicInterfaceProxy.Resolve<IDemo>();
            demo.Say();
            demo.Say("123");
            demo.Say(5, new List<string>() { "1", "2", "3" }, Kind.a);
            demo.Say("demo", 6, new List<string>() { "6" }, Kind.b);
            demo.Test = new List<string>() { "11", "12" };
            var b = demo.Test;

它会直接调用你的代理接口对象,只要有相应的传参,就可以把信息通讯给服务器。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝创精英团队

你的支持是我最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值