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

 

-结果

                         

 

posted @ 2016-09-06 10:43  蜡笔小黄  阅读(8624)  评论(3编辑  收藏  举报