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
原文转载:http://www.shaoqun.com/a/576571.html
catch:https://www.ikjzd.com/w/832
tineye:https://www.ikjzd.com/w/448
Castle是2003年诞生于ApacheAvalon项目,目的是为了创建一个IOC框架。发展到现在已经有四个组件:ORM组件:ActiveRecordIOC组件:Windsor动态代理组件:DynamicProxyWebMVC组件:MonoRail本文主要介绍动态代理组件Castle.DynamicProxy基本用法Castle.DynamicProxy是通过Emit反射动态生成代理类来实现的,
tm商标:https://www.ikjzd.com/w/1069
tineye:https://www.ikjzd.com/w/448
dmm.adult:https://www.ikjzd.com/w/2026
中美贸易战热潮下,严惩借道越南出口美国的避税方法!:https://www.ikjzd.com/home/104210
2019eBay运营分享大会(义乌)直击:欧美市场热门类目是它?:https://www.ikjzd.com/home/101792
跨境观察 | "黑五"失声 跨境进口的明天会好吗?:https://www.ikjzd.com/home/135443
没有评论:
发表评论