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

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

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

基本用法

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

基于接口的拦截器

  1. public interface IProductRepository
  2. {
  3. void Add(string name);
  4. }
  5. public class ProductRepository : IProductRepository
  6. {
  7. public void Add(string name) => Console.WriteLine($"新增产品:{name}");
  8. }
  9. public class LoggerInterceptor : IInterceptor
  10. {
  11. public void Intercept(IInvocation invocation)
  12. {
  13. var methodName = invocation.Method.Name;
  14. Console.WriteLine($"{methodName} 执行前");
  15. //调用业务方法
  16. invocation.Proceed();
  17. Console.WriteLine($"{methodName} 执行完毕");
  18. }
  19. }
  20. class Program
  21. {
  22. static void Main(string[] args)
  23. {
  24. ProxyGenerator generator = new ProxyGenerator();
  25. IInterceptor loggerIntercept = new LoggerInterceptor();
  26. IProductRepository productRepo = new ProductRepository();
  27. IProductRepository proxy = generator.CreateInterfaceProxyWithTarget(productRepo, loggerIntercept);
  28. proxy.Add("大米");
  29. Console.Read();
  30. }
  31. }

基于类的拦截器

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

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

异步函数拦截

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

  1. public class ProductRepository
  2. {
  3. public virtual Task Add(string name)
  4. {
  5. return Task.Run(() =>
  6. {
  7. Thread.Sleep(1000);
  8. Console.WriteLine($"异步新增产品:{name}");
  9. });
  10. }
  11. }
  12. public class LoggerInterceptor : IInterceptor
  13. {
  14. public async void Intercept(IInvocation invocation)
  15. {
  16. var methodName = invocation.Method.Name;
  17. Console.WriteLine($"{methodName} 执行前");
  18. invocation.Proceed();
  19. // 不 await 的话将会先输出“执行完毕”,再输出“异步新增产品”
  20. var task = (Task)invocation.ReturnValue;
  21. await task;
  22. Console.WriteLine($"{methodName} 执行完毕");
  23. }
  24. }

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

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

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

  1. public class ProductRepository : IProductRepository
  2. {
  3. public Task Add(string name)
  4. {
  5. return Task.Run(() =>
  6. {
  7. Thread.Sleep(1000);
  8. Console.WriteLine($"异步新增产品:{name}");
  9. });
  10. }
  11. public Task<string> Get()
  12. {
  13. return Task.Run(() =>
  14. {
  15. Thread.Sleep(1000);
  16. Console.WriteLine($"获取产品");
  17. return "大米";
  18. });
  19. }
  20. }
  21. public class LoggerInterceptor : IAsyncInterceptor
  22. {
  23. public void InterceptAsynchronous(IInvocation invocation)
  24. {
  25. invocation.ReturnValue = InternalInterceptAsynchronous(invocation);
  26. }
  27. async Task InternalInterceptAsynchronous(IInvocation invocation)
  28. {
  29. var methodName = invocation.Method.Name;
  30. Console.WriteLine($"{methodName} 异步执行前");
  31. invocation.Proceed();
  32. await (Task)invocation.ReturnValue;
  33. Console.WriteLine($"{methodName} 异步执行完毕");
  34. }
  35. public void InterceptAsynchronous<TResult>(IInvocation invocation)
  36. {
  37. invocation.ReturnValue = InternalInterceptAsynchronous<TResult>(invocation);
  38. Console.WriteLine(((Task<TResult>)invocation.ReturnValue).Id);
  39. }
  40. private async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation)
  41. {
  42. var methodName = invocation.Method.Name;
  43. Console.WriteLine($"{methodName} 异步执行前");
  44. invocation.Proceed();
  45. var task = (Task<TResult>)invocation.ReturnValue;
  46. TResult result = await task;
  47. Console.WriteLine(task.Id);
  48. Console.WriteLine($"{methodName} 异步执行完毕");
  49. return result;
  50. }
  51. public void InterceptSynchronous(IInvocation invocation)
  52. {
  53. var methodName = invocation.Method.Name;
  54. Console.WriteLine($"{methodName} 同步执行前");
  55. invocation.Proceed();
  56. Console.WriteLine($"{methodName} 同步执行完毕");
  57. }
  58. }
  59. class Program
  60. {
  61. static void Main(string[] args)
  62. {
  63. ProxyGenerator generator = new ProxyGenerator();
  64. IAsyncInterceptor loggerIntercept = new LoggerInterceptor();
  65. IProductRepository productRepo = new ProductRepository();
  66. IProductRepository proxy = generator.CreateInterfaceProxyWithTarget(productRepo, loggerIntercept);
  67. proxy.Get();
  68. }
  69. }

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

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

这样就挺好的。

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

Autofac 集成

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

基于接口的拦截器

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

基于类的拦截器

  1. static void Main(string[] args)
  2. {
  3. ContainerBuilder builder = new ContainerBuilder();
  4. //注册拦截器
  5. builder.RegisterType<LoggerInterceptor>().AsSelf();
  6. //注册要拦截的服务
  7. builder.RegisterType<ProductRepository>()
  8. .EnableClassInterceptors() //启用类拦截
  9. .InterceptedBy(typeof(LoggerInterceptor)); //指定拦截器
  10. IContainer container = builder.Build();
  11. ProductRepository productRepo = container.Resolve<ProductRepository>();
  12. productRepo.Add("大米");
  13. }

异步函数拦截

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

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

  1. public class LoggerInterceptor : IInterceptor
  2. {
  3. readonly LoggerAsyncInterceptor interceptor;
  4. public LoggerInterceptor(LoggerAsyncInterceptor interceptor)
  5. {
  6. this.interceptor = interceptor;
  7. }
  8. public void Intercept(IInvocation invocation)
  9. {
  10. this.interceptor.ToInterceptor().Intercept(invocation);
  11. }
  12. }
  13. public class LoggerAsyncInterceptor : IAsyncInterceptor
  14. {
  15. public void InterceptAsynchronous(IInvocation invocation)
  16. {
  17. //...
  18. }
  19. public void InterceptAsynchronous<TResult>(IInvocation invocation)
  20. {
  21. //...
  22. }
  23. public void InterceptSynchronous(IInvocation invocation)
  24. {
  25. //...
  26. }
  27. }
  28. static void Main(string[] args)
  29. {
  30. ContainerBuilder builder = new ContainerBuilder();
  31. //注册拦截器
  32. builder.RegisterType<LoggerInterceptor>().AsSelf();
  33. builder.RegisterType<LoggerAsyncInterceptor>().AsSelf();
  34. //注册要拦截的服务
  35. builder.RegisterType<ProductRepository>().AsImplementedInterfaces()
  36. .EnableInterfaceInterceptors() //启用接口拦截
  37. .InterceptedBy(typeof(LoggerInterceptor)); //指定拦截器
  38. var container = builder.Build();
  39. IProductRepository productRepo = container.Resolve<IProductRepository>();
  40. productRepo.Get();
  41. }

参考

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

使用 Castle 实现 AOP,以及 Autofac 集成 Castle的更多相关文章

  1. NET Core 整合Autofac和Castle

    NET Core 整合Autofac和Castle 阅读目录 前言: 1.ASP.NET Core中的Autofac 2.整合Castle的DynamicProxy 3.注意事项 回到目录 前言: 除 ...

  2. AOP 环绕通知 集成了前置 后置 返回通知等功能

    AOP 环绕通知 集成了前置 后置 返回通知等功能

  3. [Solution] AOP原理解析及Castle、Autofac、Unity框架使用

    本节目录: AOP介绍 AOP基本原理 AOP框架 Castle Core Castle Windsor Autofac Unity AOP介绍 面向切面编程(Aspect Oriented Prog ...

  4. AOP原理解析及Castle、Autofac、Unity框架使用

    转自:https://www.cnblogs.com/neverc/p/5241466.html AOP介绍 面向切面编程(Aspect Oriented Programming,英文缩写为AOP), ...

  5. [AOP系列]Autofac+Castle实现AOP事务

    一.前言 最近公司新项目,需要搭架构进行开发,其中需要保证事务的一致性,经过一番查找,发现很多博文都是通过Spring.Net.Unity.PostSharp.Castle Windsor这些方式实现 ...

  6. [AOP系列]Autofac+Castle实现AOP日志

    一.前言 最近公司新项目,需要搭架构进行开发,其中需要对一些日志进行输出,经过一番查找,发现很多博文都是通过Spring.Net.Unity.PostSharp.Castle Windsor这些方式实 ...

  7. ASP.NET Core 整合Autofac和Castle实现自动AOP拦截

    前言: 除了ASP.NETCore自带的IOC容器外,我们还可以使用其他成熟的DI框架,如Autofac,StructureMap等(笔者只用过Unity,Ninject和Castle). 1.ASP ...

  8. C#使用Castle实现AOP面向切面编程

    Castle.Core 本质是创建继承原来类的代理类,重写虚方法实现AOP功能.个人觉得比Autofac用着爽 使用方式比较简单,先新建一个控制台项目,然后在Nuget上搜索Castle.Core并安 ...

  9. 使用MEF与Castle实现AOP

    MEF是微软的一个ioc框架,使用非常方便,我们只需要在需要导出的类上标记[Export],在需要使用的地方[import]就可以使用了.现在我们扩展MEF,在其装配生成实例时,使用Castle Dy ...

随机推荐

  1. hbase伪分布式环境的搭建

    一,实验环境: 1, ubuntu server 16.04 2, jdk,1.8 3, hadoop 2.7.4 伪分布式环境或者集群模式 4, hbase-1.2.6.tar.gz 二,环境的搭建 ...

  2. Linux-apache httd.conf文件详解

    Linux-apache httd.conf文件详解 # This is the main Apache server configuration file. It contains the # co ...

  3. Linux Bash编程

    在Linux系统介绍中,介绍了shell的多个版本,现在的Linux发行版基本都默认使用bash(Bourne Again shell),兼容Bourne shell (sh),本文将简要介绍Bash ...

  4. Linux 防火墙相关操作

    目录 1.查看防火墙状态 2.部署防火墙 3.常用操作 4.其他操作 1.查看防火墙状态 systemctl status firewalld 绿字部分 Active:active(running) ...

  5. JVM之JVM体系结构

    JVM是运行在操作系统之上的,它与硬件没有直接的交互 下图运行时数据区灰色代表线程私有,亮色(方法区和堆)代表所有线程共享. 1.类装载器ClassLoader 负责加载class文件,class文件 ...

  6. A - The Suspects (sars传染)

    题意:有m组,0为起点,有0的那一组全是嫌疑人,之后会不断传递到其它组去,问一共有多少人是嫌疑人. Severe acute respiratory syndrome (SARS), an atypi ...

  7. Codeforces Round #515 (Div. 3) C. Books Queries (模拟)

    题意:有一个一维的书架,\(L\)表示在最左端放一本书,\(R\)表示在最右端放一本书,\(?\)表示从左数或从右数,最少数多少次才能得到要找的书. 题解:我们开一个稍微大一点的数组,从它的中间开始模 ...

  8. QT串口助手(五):文件操作

    作者:zzssdd2 E-mail:zzssdd2@foxmail.com 一.前言 开发环境:Qt5.12.10 + MinGW 功能 文件的发送 数据的保存 知识点 QFile类的使用 QTime ...

  9. 使用开源量子编程框架ProjectQ打印编译后的量子线路与绘制线路图

    技术背景 在量子计算领域,基于量子芯片的算法设计(或简称为量子算法)是基于量子线路来设计的,类似于传统计算中使用的与门和非门之类的逻辑门.因此研究一个量子线路输入后的编译(可以简化为数量更少的量子门组 ...

  10. Matching Engine For Laravel(基于redis的撮合引擎),PHP高性能撮合引擎

    Laravel Package for Matching Engine 快速开始 github地址 安装: composer require sting_bo/mengine 复制配置文件: php ...