序言

本篇文章介绍基于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[];//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 - ]; //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[];//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 - ]; //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

-结果

                 

Mono.Cecil 初探(一):实现AOP的更多相关文章

  1. C# Asp.net中的AOP框架 Microsoft.CCI, Mono.Cecil, Typemock Open-AOP API, PostSharp -摘自网络 (可以利用反射 Attribute 进行面向切面编程 可以用在记录整个方法的Log方面)

    Both Microsoft.CCI and Mono.Cecil are low-level, and don't validate produced assemblies. It takes lo ...

  2. 基于Mono.Cecil的静态注入

    Aop注入有2种方式:动态注入和静态注入,其中动态注入有很多实现了 动态注入有几种方式: 利用Remoting的ContextBoundObject或MarshalByRefObject. 动态代理( ...

  3. 编译时MSIL注入--实践Mono Cecil(1)

    原文:编译时MSIL注入--实践Mono Cecil(1) 紧接上两篇浅谈.NET编译时注入(C#-->IL)和浅谈VS编译自定义编译任务—MSBuild Task(csproject),在第一 ...

  4. 运用Mono.Cecil 反射读取.NET程序集元数据

    CLR自带的反射机智和API可以很轻松的读取.NET程序集信息,但是不能对程序集进行修改.CLR提供的是只读的API,但是开源项目Mono.Cecil不仅仅可以读取.NET程序集的元数据,还可以进行修 ...

  5. 使用Mono Cecil 动态获取运行时数据 (Atribute形式 进行注入 用于写Log) [此文报考 xxx is declared in another module and needs to be imported的解决方法]-摘自网络

    目录 一:普通写法 二:注入定义 三:Weave函数 四:参数构造 五:业务编写 六:注入调用 7.  怎么调用别的程序集的方法示例 8. [is declared in another module ...

  6. 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习)

    原文 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习) Mono.Cecil是一个强大的MSIL的注入工具,利用它可以实现动态创建程序集,也可以实现拦截器横向切入动态方法,甚至还 ...

  7. 教你怎么用Mono Cecil - 动态注入 (注意代码的注释)

    原文 教你怎么用Mono Cecil - 动态注入 (注意代码的注释) 使用 Mono Cecil 进行反编译:using Mono.Cecil; using Mono.Cecil.Cil; //.. ...

  8. 使用 Mono.Cecil 辅助 Unity3D 手游进行性能测试

    Unity3D 引擎在  UnityEngine 名字空间下,提供了  Profiler 类(Unity 5.6 开始似乎改变了这个名字空间),用于辅助对项目性能进行测试.以 Android 平台为例 ...

  9. 巧用Mono.Cecil反射加载类型和方法信息

    最近在做服务的细粒度治理,统一管理所有服务的方法.参数.返回值信息.方便后续的各个模块之间的对接和协作. 目前系统中所有的服务,管理到接口契约粒度,即服务接口声明和服务接口实现.要做服务的细粒度治理: ...

随机推荐

  1. org.apache.jasper.JasperException:省略"/html/sysmaintain/authority/user/../../module/verify_login.jsp" not found

    说明了JSP页面里引用安全登录页面的jsp路径代码:<%@ include file="../../module/verify_login.jsp"%>这句代码引用的路 ...

  2. win7 64位下 mongodb安装及命令运行

    有网友老催我把框架加上mongodb的支持,于是偶尔抽空看了看相关的文章. 今天有缘,就把mongodb安装了一下,中间遇到了小小的问题,So,把整个过程记录一下: 1:先上官网:http://www ...

  3. ESENT分布式数据存储

    关于ESENT,我能想到最恰当的比喻是,它是Microsoft世界的BerkeleyDB,鲜为人知,很少有.NET开发人员使用它,rhino-queues项目使用就是它,但它的性能和可靠性已经经受住了 ...

  4. FLEX布局的一些问题和解决方法

    前言 露珠最近研究了一下flex的布局方式,发现项w3c推出的这套布局解决方案对于日益复杂的前端开发布局来说是确实是一利器,并且在不同的屏幕上实现了真正的响应式布局:不再单纯地依赖百分比和float的 ...

  5. Orchard 微软CMS项目介绍

    我之前的项目中使用了Orchard, 它依据依赖注入的思想而做的模块化让我深深为之着迷,这里开始宣传一下这个架构. 包含的概念非常之多,我现在也不甚了解.Orchard就是自己想控制它改变它的话需要非 ...

  6. 程序员眼中的 SQL Server-执行计划教会我如何创建索引?

    先说点废话 以前有 DBA 在身边的时候,从来不曾考虑过数据库性能的问题,但是,当一个应用程序从头到脚都由自己完成,而且数据库面对的是接近百万的数据,看着一个页面加载速度像乌龟一样,自己心里真是有种挫 ...

  7. 策划编写一个新的Helper类

    https://code.csdn.net/jy02305022/blqw-data 有朋友看见的话给点意见呗

  8. eclipse下打包实践

    前提: 配置好打包相关的插件,看打包的结果分别添加不同的plugin,装好m2eclipse. 以下步骤以war包的packing为例. 步骤: 如下图:右键,选择Run As 或者 Debug As ...

  9. appledoc 使用brew命令安装使用

    appledoc --project-name yushuyi12345677 --project-company "xiaoyu123" --company-id aaaa -- ...

  10. Java 积累复习用

    1.jvm 默认编码:Java的默认编码 2.jvm heap : Java虚拟机的内存组成以及堆内存介绍 3.Java命令学习系列(一)--Jps 4.Java命令学习系列(二)--Jstack 5 ...