使用 Castle 实现 AOP,以及 Autofac 集成 Castle

  • A+
所属分类:.NET技术
摘要

Castle 是 2003 年诞生于 Apache Avalon 项目,目的是为了创建一个IOC 框架。发展到现在已经有四个组件:

Castle 是 2003 年诞生于 Apache Avalon 项目,目的是为了创建一个IOC 框架。发展到现在已经有四个组件:

  • ORM组件:ActiveRecord
  • IOC组件:Windsor
  • 动态代理组件:DynamicProxy
  • Web MVC组件:MonoRail

本文主要介绍 动态代理组件 Castle.DynamicProxy

基本用法

Castle.DynamicProxy 是通过 Emit 反射动态生成代理类来实现的,效率相对静态植入要慢一点,但比普通的反射又高一些。动态代理只对公共接口方法、类中的虚方法生效,因为只有接口中的方法、类中的虚方法才可以在子类中重写。

基于接口的拦截器

public interface IProductRepository {     void Add(string name); }  public class ProductRepository : IProductRepository {     public void Add(string name) => Console.WriteLine($"新增产品:{name}"); }  public class LoggerInterceptor : IInterceptor {     public void Intercept(IInvocation invocation)     {         var methodName = invocation.Method.Name;          Console.WriteLine($"{methodName} 执行前");          //调用业务方法         invocation.Proceed();          Console.WriteLine($"{methodName} 执行完毕");     } }  class Program {     static void Main(string[] args)     {         ProxyGenerator generator = new ProxyGenerator();          IInterceptor loggerIntercept = new LoggerInterceptor();          IProductRepository productRepo = new ProductRepository();          IProductRepository proxy = generator.CreateInterfaceProxyWithTarget(productRepo, loggerIntercept);          proxy.Add("大米");          Console.Read();     } } 

基于类的拦截器

public class ProductRepository {     public virtual void Add(string name) => Console.WriteLine($"新增产品:{name}"); }  static void Main(string[] args) {     ProxyGenerator generator = new ProxyGenerator();      IInterceptor loggerIntercept = new LoggerInterceptor();      ProductRepository proxy = generator.CreateClassProxyWithTarget(new ProductRepository(), loggerIntercept);     // 使用 CreateClassProxy 泛型方法可以省去实例化代码     //ProductRepository proxy = generator.CreateClassProxy<ProductRepository>(loggerIntercept);      proxy.Add("大米"); } 

在上例中,如果 ProductRepository.Add 不是虚方法,也不会报错,但是拦截器不会被调用。

异步函数拦截

Castle.DynamicProxy 对异步函数的拦截跟同步没啥差别,只是,如果要在方法执行完成后插入内容,需要 await

public class ProductRepository {     public virtual Task Add(string name)     {         return Task.Run(() =>                         {                             Thread.Sleep(1000);                             Console.WriteLine($"异步新增产品:{name}");                         });     } }  public class LoggerInterceptor : IInterceptor {     public async void Intercept(IInvocation invocation)     {         var methodName = invocation.Method.Name;          Console.WriteLine($"{methodName} 执行前");          invocation.Proceed();          // 不 await 的话将会先输出“执行完毕”,再输出“异步新增产品”         var task = (Task)invocation.ReturnValue;         await task;          Console.WriteLine($"{methodName} 执行完毕");     } } 

上面这个写法是简单粗暴的,如果碰到返回值是 Task<TResult>,或者不是异步函数,就会出错。所以这里是要对返回值进行一个判断的。

可以使用 Castle.Core.AsyncInterceptor 包,它包装了 Castle,使异步调用更简单。

Castle.Core.AsyncInterceptor 的 GitHub 地址:https://github.com/JSkimming/Castle.Core.AsyncInterceptor

public class ProductRepository : IProductRepository {     public Task Add(string name)     {         return Task.Run(() =>                         {                             Thread.Sleep(1000);                             Console.WriteLine($"异步新增产品:{name}");                         });     }      public Task<string> Get()     {         return Task.Run(() =>                         {                             Thread.Sleep(1000);                             Console.WriteLine($"获取产品");                              return "大米";                         });     } }  public class LoggerInterceptor : IAsyncInterceptor {     public void InterceptAsynchronous(IInvocation invocation)     {         invocation.ReturnValue = InternalInterceptAsynchronous(invocation);     }      async Task InternalInterceptAsynchronous(IInvocation invocation)     {         var methodName = invocation.Method.Name;          Console.WriteLine($"{methodName} 异步执行前");          invocation.Proceed();         await (Task)invocation.ReturnValue;          Console.WriteLine($"{methodName} 异步执行完毕");     }      public void InterceptAsynchronous<TResult>(IInvocation invocation)     {         invocation.ReturnValue = InternalInterceptAsynchronous<TResult>(invocation);          Console.WriteLine(((Task<TResult>)invocation.ReturnValue).Id);     }      private async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation)     {         var methodName = invocation.Method.Name;          Console.WriteLine($"{methodName} 异步执行前");          invocation.Proceed();         var task = (Task<TResult>)invocation.ReturnValue;         TResult result = await task;          Console.WriteLine(task.Id);          Console.WriteLine($"{methodName} 异步执行完毕");          return result;     }      public void InterceptSynchronous(IInvocation invocation)     {         var methodName = invocation.Method.Name;          Console.WriteLine($"{methodName} 同步执行前");          invocation.Proceed();          Console.WriteLine($"{methodName} 同步执行完毕");     } }  class Program {     static void Main(string[] args)     {         ProxyGenerator generator = new ProxyGenerator();          IAsyncInterceptor loggerIntercept = new LoggerInterceptor();          IProductRepository productRepo = new ProductRepository();          IProductRepository proxy = generator.CreateInterfaceProxyWithTarget(productRepo, loggerIntercept);          proxy.Get();     } } 

这是 Castle.Core.AsyncInterceptor 提供的示例写法,这里有个问题,也是我的疑惑。invocation.ReturnValue = InternalInterceptAsynchronous(invocation); 将导致代理返回的 Task 是一个新的 Task,这一点我们可以输出 Task.Id 来确认。个人感觉有点画蛇添足。

public async void InterceptAsynchronous<TResult>(IInvocation invocation) {     var methodName = invocation.Method.Name;      Console.WriteLine($"{methodName} 异步执行前");      invocation.Proceed();     var task = (Task<TResult>)invocation.ReturnValue;     await task;      Console.WriteLine($"{methodName} 异步执行完毕"); } 

这样就挺好的。

如果有小伙伴知道为什么要返回一个新的 Task,请留言告诉我,谢谢!

Autofac 集成

Autofac.Extras.DynamicProxy 是一个 Autofac 扩展,可与 Castle 一起提供 AOP 拦截。

基于接口的拦截器

static void Main(string[] args) {     ContainerBuilder builder = new ContainerBuilder();     //注册拦截器     builder.RegisterType<LoggerInterceptor>().AsSelf();      //注册要拦截的服务     builder.RegisterType<ProductRepository>().AsImplementedInterfaces()         .EnableInterfaceInterceptors()                  //启用接口拦截         .InterceptedBy(typeof(LoggerInterceptor));      //指定拦截器      IContainer container = builder.Build();      IProductRepository productRepo = container.Resolve<IProductRepository>();      productRepo.Add("大米"); } 

基于类的拦截器

static void Main(string[] args) {     ContainerBuilder builder = new ContainerBuilder();     //注册拦截器     builder.RegisterType<LoggerInterceptor>().AsSelf();      //注册要拦截的服务     builder.RegisterType<ProductRepository>()         .EnableClassInterceptors()                      //启用类拦截         .InterceptedBy(typeof(LoggerInterceptor));      //指定拦截器      IContainer container = builder.Build();      ProductRepository productRepo = container.Resolve<ProductRepository>();      productRepo.Add("大米"); } 

异步函数拦截

Castle.Core.AsyncInterceptor 中,IAsyncInterceptor 接口并不集成 IInterceptor 接口,而 Autofac.Extras.DynamicProxy 是绑定 Castle 的,所以按上面同步拦截的写法是会报错的。

IAsyncInterceptor 提供了 ToInterceptor() 扩展方法来进行类型转换。

public class LoggerInterceptor : IInterceptor {     readonly LoggerAsyncInterceptor interceptor;      public LoggerInterceptor(LoggerAsyncInterceptor interceptor)     {         this.interceptor = interceptor;     }      public void Intercept(IInvocation invocation)     {         this.interceptor.ToInterceptor().Intercept(invocation);     } }  public class LoggerAsyncInterceptor : IAsyncInterceptor {     public void InterceptAsynchronous(IInvocation invocation)     {         //...     }      public void InterceptAsynchronous<TResult>(IInvocation invocation)     {         //...     }      public void InterceptSynchronous(IInvocation invocation)     {         //...     } }  static void Main(string[] args) {     ContainerBuilder builder = new ContainerBuilder();     //注册拦截器     builder.RegisterType<LoggerInterceptor>().AsSelf();     builder.RegisterType<LoggerAsyncInterceptor>().AsSelf();      //注册要拦截的服务     builder.RegisterType<ProductRepository>().AsImplementedInterfaces()         .EnableInterfaceInterceptors()                  //启用接口拦截         .InterceptedBy(typeof(LoggerInterceptor));      //指定拦截器      var container = builder.Build();      IProductRepository productRepo = container.Resolve<IProductRepository>();      productRepo.Get(); } 

参考

https://www.cnblogs.com/youring2/p/10962573.html