Mono.Cecil 初探(一):實現AOP


序言

本篇文章介紹基於Mono.Cecil實現靜態AOP的兩種方式:無交互AOP和交互式AOP。

概念介紹

Mono.Cecil:一個可加載並瀏覽現有程序集並進行動態修改並保存的.NET框架。

AOP:面向切面編程。可以簡單理解為程序中的每個類的方法均是一塊“積木”,采用AOP把新增的“積木隨心所欲地嵌入”到各個“積木”上面(前面)或下面(后面)。如下圖所示:

                                                                                                 

動態AOP:在運行時進行AOP。.NET現有.Net Remoting,Unity,Spring.NET,PostSharp,Mr Advice等多種框架可供使用。

靜態AOP:相對於動態AOP,靜態AOP是指在編譯時、運行前就已經進行了AOP。.NET中一般通過修改編譯生成的中間語言IL實現,Mono.Cecil就是實現靜態AOP一個很好的方式。根據與原有程序交互的情況,本文把靜態AOP分為無交互AOP和交互式AOP兩種方式。

無交互AOP

與原有程序無任何“交集”,純粹式的AOP。下面通過兩個例子進行說明如何通過Mono.Cecil進行無交互AOP。

同一個方法內AOP

原程序:控制台打印出“Hello World”。代碼如下:

class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World");
        }
    }

  

AOP需求:需要在打印前和打印后輸出當前時間。

-添加Mono.Cecil.dll引用並添加以下代碼

using Mono.Cecil;
using Mono.Cecil.Cil;

  

-定位方法(建議用Linq)

        AssemblyDefinition assembiy = AssemblyDefinition.ReadAssembly(Path);//Path: dll or exe Path
              var method = assembiy.MainModule
                .Types.FirstOrDefault(t => t.Name == "Program")
                .Methods.FirstOrDefault(m => m.Name == "Main");

  

-獲取IL

var worker = method.Body.GetILProcessor();//Get IL

-AOP Front

                string FrontTime = DateTime.Now.ToString();
                var ins = method.Body.Instructions[0];//Get First IL Step 
                worker.InsertBefore(ins, worker.Create(OpCodes.Ldstr, FrontTime));
                worker.InsertBefore(ins, worker.Create(OpCodes.Call,
                assembiy.MainModule.Import(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }))));

-AOP Back

                string BackTime = DateTime.Now.ToString();
                ins = method.Body.Instructions[method.Body.Instructions.Count - 1]; //Get Lastest IL Step
                worker.InsertBefore(ins, worker.Create(OpCodes.Ldstr, BackTime));
                worker.InsertBefore(ins, worker.Create(OpCodes.Call,
                assembiy.MainModule.Import(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }))));

  

-保存修改

assembiy.Write(Path);

-結果

采用Refactor進行對比得知AOP已成功!

         

 

“跨類”AOP

此種方式指的是在方法前后通過指定調用其他類的方法實現AOP,可用於擴展功能,如日志記錄,數據庫記錄等。

相對於同一個方法內的AOP,因為通過方法調用指定類的方法,實現更加靈活,功能擴展更加全面且在開發階段可進行調試或單元測試,所以此種方式應用層面更為廣泛。

下面將從靜態和非靜態兩種方式進行代碼實現。

原程序:控制台打印出“Hello World”

 class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World");
        }
    }

AOP需求:需要把打印前的時間和打印后的時間記錄到數據庫中。

跨靜態類/靜態方法AOP

為示例簡便,LogDT方法表示記錄時間到數據庫中(為方便顯示,采用控制台打印的方式)

 

public  class LogDateTime_Static
    {
      public static void LogDT()
      {
          Console.WriteLine(DateTime.Now.ToString());
      }
    }

 

-AOP Front

//AOP_Front
                var ins = method.Body.Instructions[0];//Get First IL Step 
                worker.InsertBefore(ins, worker.Create(OpCodes.Call,
                assembiy.MainModule.Import(typeof(LogDateTime).GetMethod("LogDT"))));//Call static Method

-AOP Back

//AOP_Back
                ins = method.Body.Instructions[method.Body.Instructions.Count - 1]; //Get Lastest IL Step
                worker.InsertBefore(ins, worker.Create(OpCodes.Call,
                assembiy.MainModule.Import(typeof(LogDateTime).GetMethod("LogDT"))));//Call static Method

-結果

      

跨非靜態類AOP

非靜態類實例代碼如下:

public  class LogDateTime_NonStatic
    {
      public void LogDT()
      {
          Console.WriteLine(DateTime.Now.ToString());
      }
    }

-實例化指定類

 var Constructor = assembiy.MainModule.Import(typeof(LogDateTime_NonStatic).GetConstructor(new Type[] { }));//Create Instance

-AOP Front

var ins = method.Body.Instructions[0];//Get First IL Step 
                worker.InsertBefore(ins, worker.Create(OpCodes.Newobj, Constructor));
                worker.InsertBefore(ins, worker.Create(OpCodes.Call,
                    assembiy.MainModule.Import(typeof(LogDateTime_NonStatic).GetMethod("LogDT"))));////Call Instance Method

-AOP Back

 ins = method.Body.Instructions[method.Body.Instructions.Count - 1]; //Get Lastest IL Step
                worker.InsertBefore(ins, worker.Create(OpCodes.Newobj, Constructor));
                worker.InsertBefore(ins, worker.Create(OpCodes.Call,
                   assembiy.MainModule.Import(typeof(LogDateTime_NonStatic).GetMethod("LogDT"))));////Call Instance Method

 

-結果

                         

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM