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

    -结果

                             

  • 相关阅读:
    界面控件DevExpress WPF入门 表达式编辑器功能
    Telerik UI for WPF全新版本——拥有Office2019高对比度主题
    DevExpress报表控件v21.2 全新的Visual Studio报表设计器
    报告生成器FastReport .NET入门指南 在Linux中启动应用程序
    文档控件DevExpress Office File API v21.2 自定义字体加载引擎
    UI组件库Kendo UI for Angular入门 如何开始使用图表功能
    WPF界面工具Telerik UI for WPF入门级教程 设置一个主题(二)
    DevExtreme初级入门教程(React篇) TypeScript支持
    报表开发利器FastReport .NET v2022.1 添加关键对象和属性
    python项目打包(一) setup.py、Python源代码项目结构
  • 原文地址:https://www.cnblogs.com/huangmianwu/p/5833924.html
Copyright © 2011-2022 走看看