使用 Castle 实现 AOP,以及 Autofac 集成 Castle
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
使用 Castle 实现 AOP,以及 Autofac 集成 Castle的更多相关文章
- NET Core 整合Autofac和Castle
NET Core 整合Autofac和Castle 阅读目录 前言: 1.ASP.NET Core中的Autofac 2.整合Castle的DynamicProxy 3.注意事项 回到目录 前言: 除 ...
- AOP 环绕通知 集成了前置 后置 返回通知等功能
AOP 环绕通知 集成了前置 后置 返回通知等功能
- [Solution] AOP原理解析及Castle、Autofac、Unity框架使用
本节目录: AOP介绍 AOP基本原理 AOP框架 Castle Core Castle Windsor Autofac Unity AOP介绍 面向切面编程(Aspect Oriented Prog ...
- AOP原理解析及Castle、Autofac、Unity框架使用
转自:https://www.cnblogs.com/neverc/p/5241466.html AOP介绍 面向切面编程(Aspect Oriented Programming,英文缩写为AOP), ...
- [AOP系列]Autofac+Castle实现AOP事务
一.前言 最近公司新项目,需要搭架构进行开发,其中需要保证事务的一致性,经过一番查找,发现很多博文都是通过Spring.Net.Unity.PostSharp.Castle Windsor这些方式实现 ...
- [AOP系列]Autofac+Castle实现AOP日志
一.前言 最近公司新项目,需要搭架构进行开发,其中需要对一些日志进行输出,经过一番查找,发现很多博文都是通过Spring.Net.Unity.PostSharp.Castle Windsor这些方式实 ...
- ASP.NET Core 整合Autofac和Castle实现自动AOP拦截
前言: 除了ASP.NETCore自带的IOC容器外,我们还可以使用其他成熟的DI框架,如Autofac,StructureMap等(笔者只用过Unity,Ninject和Castle). 1.ASP ...
- C#使用Castle实现AOP面向切面编程
Castle.Core 本质是创建继承原来类的代理类,重写虚方法实现AOP功能.个人觉得比Autofac用着爽 使用方式比较简单,先新建一个控制台项目,然后在Nuget上搜索Castle.Core并安 ...
- 使用MEF与Castle实现AOP
MEF是微软的一个ioc框架,使用非常方便,我们只需要在需要导出的类上标记[Export],在需要使用的地方[import]就可以使用了.现在我们扩展MEF,在其装配生成实例时,使用Castle Dy ...
随机推荐
- Java开发中POJO和JSON互转时如何忽略隐藏字段
1. 前言 在Java开发中有时候某些敏感信息我们需要屏蔽掉,不能被消费这些数据的客户端知道.通常情况下我们会将其设置为null或者空字符 "",其实还有其它办法,如果你使用了Ja ...
- Linux 查找文件的正确方式
Linux 系统中查找文件的命令有 which.whereis.locate 和 find 等,本文对这四条命令进行简单的介绍.列举了一些简单的使用方式. which 在 PATH 变量中定义的全部路 ...
- vue开发东京买菜,全栈项目,前端django,带手机GPS精准定位,带发票系统,带快递系统,带微信/支付宝/花呗/银行卡支付/带手机号一键登陆,等等
因为博客园不能发视频,所以,完整的视频,开发文档,源码,请向博主索取 完整视频+开发文档+源码,duanshuiLu.com下载 vue+django手机购物商城APP,带支付,带GPS精准定位用户, ...
- 数据结构-kmp算法
定义 改进字符串的匹配算法 关键:通过实现一个包含了模式串的局部匹配信息的next()函数,利用匹配失败的信息,减少匹配次数. 1.BF算法 暴力匹配 给定 文本串S "BBC ABCDAB ...
- A - Promotions
题目详见http://7xjob4.com1.z0.glb.clouddn.com/3f644de6844d64706eb36baa3a0c27b0 这题是普通的拓扑排序,要把每一层的都保存下来. # ...
- codeforces251A. Points on Line
Little Petya likes points a lot. Recently his mom has presented him n points lying on the line OX. N ...
- 郁闷的出纳员 HYSBZ - 1503
OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的 工资.这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常调整员工的工资.如果他 ...
- 抓取QQ音乐歌单
抓取QQ音乐歌单1.通过分析歌曲下载路径来分析所需参数: 通过比较, 得出其中歌曲下载url与参数vkey是可变的,歌曲下载url中可变得值是请求歌单返回的歌曲数据的strMediaMid参数, 而v ...
- bfs输出路径 && 最短路(迪杰斯特拉)输出路径
问题描述 解决方法 1.像第一个问题那就是最短路问题(我代码采用迪杰斯特拉算法)实现 2.换乘次数最少,那就用bfs广搜来寻找答案.但是我的代码不能保证这个最少换乘是最短路程 代码 1 #includ ...
- Win7下安装IIS
安装IIS 1.控制面板 --> 程序 --> 卸载程序,进入"程序与功能". 2.进入"打开或关闭Window功能". 3.找到"Int ...