AOP的本质是方法拦截(将针对目标方法调用劫持下来,进而执行执行的操作),至于方法拦截的实现方案,不外乎两种代码注入类型,即编译时的静态注入和运行时的动态注入,本篇文章列出了几种常用的动态注入方案。这篇文章的目标并不是提供完整的AOP框架的解决方案,而是说明各种解决方案后面的原理,所以我们提供的实例代码会尽可能简单。为了确定拦截操作是否执行,我们定义了如下这个Indicator类型,我们的拦截操作会将其静态属性Injected属性设置为True,我们演示的代码最终通过这个属性来确定拦截是否成功。
public static class Indicator
{
public static bool Injected { get; set; }
}
一、IL Emit(接口)
IL Emit是实现AOP的首选方案。如果方法调用时针对接口完成,我们可以生成一个代理类型来封装对象,并且这个代理类型同时实现目标接口,那么只要我们能够将针对目标对象的方法调用转换成针对代理对象的调用,就能实现针对目标对象的方法拦截。举个简单的例子,Foobar实现了IFoobar接口,如果我们需要拦截接口方法Invoke,我们可以生成一个FoobarProxy类型。如代码片段所示,FoobarProxy封装了一个IFoobar对象,并实现了IFoobar接口。在实现的Invoke方法中,它在调用封装对象的同名方法之前率先执行了拦截操作。
public interface IFoobar
{
int Invoke();
}
public class Foobar : IFoobar
{
public int Invoke() => 1;
}
public class FoobarProxy : IFoobar
{
private readonly IFoobar _target;
public FoobarProxy(IFoobar target)=>_target = target
public int Invoke()
{
Indicator.Injected = true;
return _target.Invoke();
}
}
上述的这个FoobarProxy类型就可以按照如下的方式利用GenerateProxyClass方法来生成。在Main方法中,我们创建一个Foobar对象,让据此创建这个动态生成的FoobarProxy,当该对象的Invoke方法执行的时候,我们期望的拦截操作自然会自动执行。
class Program
{
static void Main(string[] args)
{
var foobar = new Foobar();
var proxy = (IFoobar)Activator.CreateInstance(GenerateProxyClass(), foobar);
Debug.Assert(Indicator.Injected == false);
Debug.Assert(proxy.Invoke() == 1);
Debug.Assert(Indicator.Injected == true);
}
static Type GenerateProxyClass()
{
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Proxy"), AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynami