zoukankan      html  css  js  c++  java
  • 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

    -结果

                             

  • 相关阅读:
    并发编程三、线程可见性的底层原理
    并发编程二、线程的安全性和线程通信
    并发编程一、多线程的意义和使用
    Java进程监控
    分布式消息通信之RabbitMQ_Note
    分布式消息通信之RabbitMQ_02
    分布式消息通信之RabbitMQ_01
    分布式消息通信之RabbitMQ Tutorials
    SpringMVC重点分析
    Apache POI 4.0.1版本读取本地Excel文件并写入数据库(兼容 xls 和 xlsx)(五)
  • 原文地址:https://www.cnblogs.com/huangmianwu/p/5833924.html
Copyright © 2011-2022 走看看