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的更多相关文章

  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. java切割~~百万 十万 万 千 百 十 个 角 分

    /** * @param value * @return */ @SuppressWarnings("unused") public static void convertLoan ...

  2. python--函数、参数、名称空间与作用域、匿名函数、内置函数、闭包

    python函数 函数定义 def welcome(): print('hello world!!') welcome() #函数调用 ...运行结果 hello world!! 函数定义和编写原则: ...

  3. dedecms万能SQL标签使用方法大全

    注意:dede_archives这是系统默认的数据库表,如果你修改过表前缀dede_,请自行更改表名.在以下示例的标签中,有一部分只写出了SQL语句,具体的完整标签写法请参考:织梦SQL标签调用方法. ...

  4. C++typename的由来和用法

  5. 【noi 2.6_9283】&【poj 3088】Push Botton Lock(DP--排列组合 Stirling数)

    题意:N个编号为1~N的数,选任意个数分入任意个盒子内(盒子互不相同)的不同排列组合数. 解法:综合排列组合 Stirling(斯特林)数的知识进行DP.C[i][j]表示组合,从i个数中选j个数的方 ...

  6. python+selenium+bs4爬取百度文库内文字 && selenium 元素可以定位到,但是无法点击问题 && pycharm多行缩进、左移

    先说一下可能用到的一些python知识 一.python中使用的是unicode编码, 而日常文本使用各类编码如:gbk utf-8 等等所以使用python进行文字读写操作时候经常会出现各种错误, ...

  7. AtCoder Beginner Contest 173 C - H and V (二进制枚举)

    题意:有一张图,.表示白色,#表示黑色,每次可以将多行和多列涂成红色(也可不涂),问有多少种方案,使得剩下黑点的个数为\(k\). 题解:又学到了新的算法qwq,因为这题的数据范围很小,所以可以用二进 ...

  8. CF1471-C. Strange Birthday Party

    CF1471-C. Strange Birthday Party 题意: 你要举办一场生日派对.派对有\(n\)个人,每个人都有一个数字\(k_i\).超市有\(m\)件礼物,购买每件礼物需要花费\( ...

  9. BIM轻量化——浏览器展示

    此篇博客仅为记录,记录钻研过程的零碎思路.         之前考虑过很多可能性,对rvt文件转换格式:.obj.JSON..gltf等等.这些可能性前人一般都尝试过,而且也都做出来了东西.     ...

  10. 阿里云 MaxCompute(ODPS)

    大数据产品架构 BASE - Dataworks ODPS - MaxCompute ODPS 功能组成(Open Data Process Service) ODPS 是旧称,阿里云公有云服务中现称 ...