1、背景html
在某些狀況下,咱們須要植入AOP代碼的類並無默認構造函數。那麼此時動態生成的代理類也須要相同簽名的構造函數,而且內部調用原始類的構造函數。本身折騰了1晚上沒搞定,如今搞定了發出來供你們一塊兒學習探討。程序員
2、梳理功能點微信
在已支持經過默認構造函數進行AOP代碼植入的狀況下(之前發過一篇博文,傳送門:你們一塊兒Aop),實現該功能咱們須要作的是:架構
1.如何經過獲取原始類的構造函數參數列表,並使用Emit生成代理類的相應構造函數。分佈式
2.如何建立並保存實例化代理類的委託,加快實例化速度。ide
3、實現方案函數
功能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點,ui
①定義構造函數的時候須要指定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 }
大功告成~,再進行一些簡單的重構。測試效果:
4、收尾
源碼附上:源碼+Demo在此!
以爲有用記得點贊哦~
做者: Zachary
出處:https://zacharyfan.com/archives/89.html
▶關於做者:張帆(Zachary,我的微信號:Zachary-ZF)。堅持用心打磨每一篇高質量原創。歡迎掃描右側的二維碼~。
按期發表原創內容:架構設計丨分佈式系統丨產品丨運營丨一些思考。
若是你是初級程序員,想提高但不知道如何下手。又或者作程序員多年,陷入了一些瓶頸想拓寬一下視野。歡迎關注個人公衆號「跨界架構師」,回覆「技術」,送你一份我長期收集和整理的思惟導圖。
若是你是運營,面對不斷變化的市場一籌莫展。又或者想了解主流的運營策略,以豐富本身的「倉庫」。歡迎關注個人公衆號「跨界架構師」,回覆「運營」,送你一份我長期收集和整理的思惟導圖。