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”。代码如下:

1
2
3
4
5
6
7
class  Program
     {
         static  void  Main( string [] args)
         {
             Console.WriteLine( "Hello World" );
         }
     }

  

AOP需求:需要在打印前和打印后输出当前时间。

-添加Mono.Cecil.dll引用并添加以下代码

1
2
using  Mono.Cecil;
using  Mono.Cecil.Cil;

  

-定位方法(建议用Linq)

1
2
3
4
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

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

-AOP Front

1
2
3
4
5
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

1
2
3
4
5
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 ) }))));

  

-保存修改

1
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

 

复制代码

-结果

                         

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值