• Aop动态生成代理类时支持带参数构造函数


    一、背景

      在某些情况下,我们需要植入AOP代码的类并没有默认构造函数。那么此时动态生成的代理类也需要相同签名的构造函数,并且内部调用原始类的构造函数。自己折腾了1晚上没搞定,现在搞定了发出来供大家一起学习探讨。

    二、梳理功能点

      在已支持通过默认构造函数进行AOP代码植入的情况下(以前发过一篇博文,传送门:大家一起Aop),实现该功能我们需要做的是:

      1.如何通过获取原始类的构造函数参数列表,并使用Emit生成代理类的相应构造函数。

      2.如何创建并保存实例化代理类的委托,加快实例化速度。

    三、实现方案

      功能1:

      在原来的生成代理类,代理类中的方法处增加生成构造函数的代码。代码很简单,如下:  

     1             if (_parameters == null || _parameters.Length == 0)
     2             {
     3                 return typeBuilder;
     4             }
     5 
     6             var baseConstructor = _originalType.GetConstructor(_parameterTypes);
     7             if (baseConstructor == null)
     8                 throw new MissingMethodException("未找到相应参数的构造函数");
     9 
    10             const MethodAttributes METHOD_ATTRIBUTES = MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Public;
    11             var constructor = typeBuilder.DefineConstructor(METHOD_ATTRIBUTES, CallingConventions.HasThis, _parameterTypes);
    12 
    13             var il = constructor.GetILGenerator();
    14 
    15             il.Emit(OpCodes.Ldarg_0);
    16 
    17             for (int i = 1; i <= _parameters.Length; i++)  //这里要注意下,索引为0的参数是当前对象本身
    18             {
    19                 il.Emit(OpCodes.Ldarg, i);
    20             }
    21 
    22             il.Emit(OpCodes.Call, baseConstructor);
    23             il.Emit(OpCodes.Ret);
    24 
    25             return typeBuilder;

      其中要注意2点,

      ①定义构造函数的时候需要指定MethodAttributes,平常我们代码编写的公有构造函数就是上面写的4个。

      ②因为我们定义的是构造函数,所以此处必须指定CallingConventions.HasThis。至于为什么去看msdn的解释,不在我们本次讨论范围内。

      

      到这里我们的动态类的构造已经完成了,接下去解决功能2:

      这里只要在原先直接取默认构造函数的地方增加一个判断,获取指定参数的构造函数来构造委托。下面贴代码:

     1         private DynamicMethod GetConstructorMethod<TReturnType>(Type aopType)
     2         {
     3             var method = _parameters == null || _parameters.Length == 0
     4                         ? new DynamicMethod("", typeof(TReturnType), null)
     5                         : new DynamicMethod("", typeof(TReturnType), _parameterTypes);
     6 
     7             var il = method.GetILGenerator();
     8 
     9             BuildConstructorIl(il, aopType);
    10 
    11             return method;
    12         }
    13  
    14  
    15          private void BuildConstructorIl(ILGenerator il, Type aopType)
    16          {
    17              if (_parameters == null || _parameters.Length == 0)
    18              {
    19                  ConstructorInfo info = aopType.GetConstructor(Type.EmptyTypes);
    20                  if (info == null)
    21                      return;
    22  
    23                  il.Emit(OpCodes.Newobj, info);
    24                  il.Emit(OpCodes.Ret);
    25              }
    26              else
    27              {
    28                  ConstructorInfo info = aopType.GetConstructor(_parameterTypes);
    29                  if (info == null)
    30                      return;
    31  
    32                  var localInstance = il.DeclareLocal(aopType);
    33                  for (int i = 0; i < _parameterTypes.Length; i++) //这里与上面的标红不同,这里因为是直接定义了一个方法,并且不存在实例,所以此处参数从索引0开始
    34                      il.Emit(OpCodes.Ldarg, i);
    35  
    36                  il.Emit(OpCodes.Newobj, info);
    37  
    38                  il.Emit(OpCodes.Stloc, localInstance);
    39                  il.Emit(OpCodes.Ldloc, localInstance);
    40                  il.Emit(OpCodes.Ret);
    41              }
    42          }

      这里需要的注意的点已经标红出来了。但这里仅是核心代码,在外层再封装几个重载用于生成不同的Func<>。

     1         public Func<TReturnType> CreateFunc<TReturnType>(Type aopType)
     2         {
     3             var method = GetConstructorMethod<TReturnType>(aopType);
     4 
     5             return method.CreateDelegate(typeof(Func<TReturnType>)) as Func<TReturnType>;
     6         }
     7 
     8         public Func<TP1, TReturnType> CreateFunc<TP1, TReturnType>(Type aopType)
     9         {
    10             var method = GetConstructorMethod<TReturnType>(aopType);
    11 
    12             return method.CreateDelegate(typeof(Func<TP1, TReturnType>)) as Func<TP1, TReturnType>;
    13         }
    14 
    15         public Func<TP1, TP2, TReturnType> CreateFunc<TP1, TP2, TReturnType>(Type aopType)
    16         {
    17             var method = GetConstructorMethod<TReturnType>(aopType);
    18 
    19             return method.CreateDelegate(typeof(Func<TP1, TP2, TReturnType>)) as Func<TP1, TP2, TReturnType>;
    20         }

    大功告成~,再进行一些简单的重构。测试效果:

    四、收尾

      源码附上:源码+Demo在此!

      觉得有用记得点赞哦~

    作者:  Zachary
    出处:https://zacharyfan.com/archives/89.html

    ▶关于作者:张帆(Zachary,个人微信号:Zachary-ZF)。坚持用心打磨每一篇高质量原创。欢迎扫描右侧的二维码~。

    定期发表原创内容:架构设计丨分布式系统丨产品丨运营丨一些思考。

    如果你是初级程序员,想提升但不知道如何下手。又或者做程序员多年,陷入了一些瓶颈想拓宽一下视野。欢迎关注我的公众号「跨界架构师」,回复「技术」,送你一份我长期收集和整理的思维导图。

    如果你是运营,面对不断变化的市场束手无策。又或者想了解主流的运营策略,以丰富自己的“仓库”。欢迎关注我的公众号「跨界架构师」,回复「运营」,送你一份我长期收集和整理的思维导图。

  • 相关阅读:
    简单工厂、工厂方法、抽象工厂
    c#之反射总结
    设计模式Builder(建造者)模式
    c#之委托和事件的区别
    c#之委托总结
    javascript之流程控制 和函数的容易忽略点
    javascript之六种数据类型以及特殊注意点
    Sublime Text 快捷键
    链接中获取文件名
    js 获取当天23点59分59秒 时间戳 (最简单的方法)
  • 原文地址:https://www.cnblogs.com/Zachary-Fan/p/5629992.html
Copyright © 2020-2023  润新知