在C#中使用CastleDynamicProxy 实现AOP
原文链接:Aspect Oriented Programming (AOP) in C# using CastleDynamicProxy
本文主要展示在C#中如何使用Castle DynamicProxy来解耦logging体系
简介
在这篇文章中,我将带你在.NET环境中实现Aspect-Oriented Programming (AOP) ,演示如何使用Castle DynamicProxy创建一个切面。在开始之前,我简单的介绍下AOP and IoC,如果你已经熟悉这些概念,你可以直接跳过下面的步骤。
什么是AOP?
Aspect-oriented programming (AOP) 面向切面编程是一个编程范式,以允许增加模块化横切关注点的分离。An aspect (方面,AOP概念中的术语,为了不混淆,下文使用英文表示,不做中文翻译)是一种常见的功能,通常是分散在类和对象层次结构中的公用方法。这些行为看起来有结构,但使用传统的面向对象编程(OOP)无法找到一个方式来表达它。
aspect一个比较贴切的例子是本文中将要讨论的日志记录功能,通常你需要在代码中编写日志,但是日志功能通常并不属于领域对象的职责。
使用AOP方法,我们可以创建这些aspects的横切关注点,并使用多种技术将它们集中附加到领域对象。IL代码编织和拦截是广泛使用的方法。在本文中,我将带你使用Castel Windsor动态框架来处理aspects的创建和应用。
控制反转(Inversion of control=Ioc)/依赖注入容器(DI Container)
IoC容器是一个用来在需要时自动创建和依赖项注入的框架。DI容器可以帮助我们以简单和更有效的方式管理应用程序中的依赖项
大多数的主流的DI(依赖注入)容器具有拦截的内置支持。这是一项高级技术,使用它可以拦截方法调用并改变在运行时期间的域对象的行为。我们将利用此功能附加aspects到我们的域对象。我选择的DI框架是Castle Windsor ,它的 DynamicProxy ,是比较流行的应用aspects的方法之一。
在CodeProject有很多很好的文章和博客提供了关于(IoC)主题更详细的资料。对IoC更大范围的详细讨论不在本文的范围内。
使用Castle DynamicProxy进行拦截
Castle DynamicProxy是一个在运行时生成.NET代理的库。 它允许你动态地更改和扩展业务对象的行为。因为横切关注点与核心领域模型的彻底解耦使得你的域模型更容易维护。如果为任何组件指定拦截,Castle 会自动创建代理。可以使用拦截器注入特定行为到代理中。
你可能会好奇这整件事的内部如何工作。每当调用方请求业务对象(具体类),IoC容器在DynamicProxy的帮助下,解析并将其包装在一个包含指定的拦截器的代理对象中。然后容器返回代理对象到调用方。然后,调用方直接与代理进行交互。 代理截取每个针对业务对象的方法调用,并让请求按照流程通过拦截器传递

下图显示请求如何进入代理。您可以看到请求在实际执行方法之前和之后都要通过所有拦截器。

在项目中使用 Castle DynamicProxy的步骤
· 使用Nuget下载和安装 ‘Castle.Windsor’ 包.
· 实现 IInterceptor 接口. 这是要被 DynamicProxy的接口.
- 实现IRegistration 接口并且注册组件. 注册拦截器其次是业务组件。指定每个业务组件要使用的拦截器。
- 为Windsor创建静态实例容器(IWindsorContainer),用该组件注册信息初始化它。.
这几乎是Castle DynamicProxy需要的所有配置
开始编码
晴朗的一天在班加罗尔微风习习,这样的天气条件适合发射火箭!让我们开始我们的示例应用程序。 此应用程序包含业务对象'Rocket'我们使用一个控制台应用程序来模拟火箭的发射。
接口包含一个签名叫” Launch”的行为。
public interface IRocket
{
void Launch(int delaySeconds);
}
然后我们来实现接口中的”Launch”方法。
public class Rocket : IRocket
{
public string Name { get; set; }
public string Model { get; set; }
public void Launch(int delaySeconds)
{
Console.WriteLine(string.Format(" {0} 秒收启动火箭发射", delaySeconds));
Thread.Sleep(1000 * delaySeconds);
Console.WriteLine("恭喜, 你的火箭发射成功");
}
}
是时候来创建我们第一个拦截器了.我们需要继承实现IInterceptor ,这个接口最终会被DynamicProxy使用.
正如你在下面看到的,我们在方法进入时进行日志记录,通过调用invocation.Proceed()来执行真正的业务行为,然后在方法执行成功后、发生异常和退出时分别进行了日志记录。我们不在需要在我们的业务模型中编写相关日志记录的代码。我们只需要将LoggingInterceptor附加到需要日志记录的组件上。
internal class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var methodName = invocation.Method.Name;
try
{
Console.WriteLine(string.Format("执行方法:{0}, 参数: {1}", methodName, string.Join(",", invocation.Arguments)));
invocation.Proceed();
Console.WriteLine(string.Format("成功执行了方法:{0}", methodName));
}
catch (Exception e)
{
Console.WriteLine(string.Format("方法:{0}, 异常信息:{1}", methodName, e.Message));
throw;
}
finally
{
Console.WriteLine(string.Format("退出方法:{0}", methodName));
}
}
}
DynamicProxy 代理提供的对象是非常有用的。它使你可以访问当前方法的方法信息、参数、返回代码值和许多其他细节,如你下面看到的。.
public interface IInvocation
{
object[] Arguments { get; }
Type[] GenericArguments { get; }
object InvocationTarget { get; }
MethodInfo Method { get; }
MethodInfo MethodInvocationTarget { get; }
object Proxy { get; }
object ReturnValue { get; set; }
Type TargetType { get; }
object GetArgumentValue(int index);
MethodInfo GetConcreteMethod();
MethodInfo GetConcreteMethodInvocationTarget();
void Proceed();
void SetArgumentValue(int index, object value);
}
实现IRegistration 接口并注册组件. 注册拦截器其次是业务组件。指定每个业务组件要使用的拦截器。正如您可能已经注意到,LoggingInterceptor被附加到我们唯一的业务组件的Rocket中
public class ComponentRegistration : IRegistration
{
public void Register(IKernelInternal kernel)
{
kernel.Register(
Component.For<LoggingInterceptor>()
.ImplementedBy<LoggingInterceptor>());
kernel.Register(
Component.For<IRocket>()
.ImplementedBy<Rocket>()
.Interceptors(InterceptorReference.ForType<LoggingInterceptor>()).Anywhere);
}
}
为 Windsor container (IWindsorContainer)创建静态实例, 使用组件注册信息对它进行初始化(ComponentRegistration)。
public class DependencyResolver
{
private static IWindsorContainer _container;
//Initialize the container
public static void Initialize()
{
_container = new WindsorContainer();
_container.Register(new ComponentRegistration());
}
//Resolve types
public static T For<T>()
{
return _container.Resolve<T>();
}
}
在控制台应用中启动应用程序
private static void Main(string[] args)
{
DependencyResolver.Initialize();
//resolve the type:Rocket
var rocket = DependencyResolver.For<IRocket>();
//method call
try
{
rocket.Launch(5);
}
catch (Exception ex)
{
}
System.Console.ReadKey();
}
让我们看一下控制台的输出。 不出所料,我们的LoggingInterceptor拦截方法调用并自动记录方法进入和退出。由于动态代理!. Thanks to DynamicProxy !

知识点
这是一个介绍性的文章,使初学者和中级开发人员理解使用 Castle Windsor DynamicProxy来理解AOP的基本概念。在未来的几天,我将不断更新这篇文章,以演示如何在一个Web Api项目中使用Log4net和动态代理在实现此日志记录解决方案
在C#中使用CastleDynamicProxy 实现AOP的更多相关文章
- Castle框架中的IOC和AOP机制
反转控制(IOC)和面向切面编程(AOP)技术作为当前比较流行的技术,其优势已受到广泛关注,但是这两项新技术在实际项目上的应用研究却很落后,而且在.NET平台下实现这两项技术没有形成可以广泛套用的框架 ...
- 理解Spring中的IOC和AOP
我们是在使用Spring框架的过程中,其实就是为了使用IOC,依赖注入和AOP,面向切面编程,这两个是Spring的灵魂. 主要用到的设计模式有工厂模式和代理模式 IOC就是典型的工厂模式,通过ses ...
- 详谈 Spring 中的 IOC 和 AOP
这篇文章主要讲 Spring 中的几个点,Spring 中的 IOC,AOP,下一篇说说 Spring 中的事务操作,注解和 XML 配置. Spring 简介 Spring 是一个开源的轻量级的企业 ...
- Spring中基于xml的AOP
1.Aop 全程是Aspect Oriented Programming 即面向切面编程,通过预编译方式和运行期动态代理实现程序功能的同一维护的一种技术.Aop是oop的延续,是软件开发中的 一个热点 ...
- .Net中的RealProxy实现AOP
序言 这个AOP要从我们公司的一个事故说起,前段时间公司的系统突然在乌云中出现,数据被泄露的一览无余,乌云上显示是SQL注入攻击.呵,多么贴近生活的一个露洞,可谓是人尽皆知啊.然而却华丽丽的给拉我们一 ...
- Spring中的IOC\DI\AOP等概念的简单学习
IoC(Inversion of Control,控制反转).这是spring的核心,贯穿始终, 所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系.Spr ...
- Spring AOP报错处理 Can not set field to $Proxy 在spring中使用事物或AOP遇到的错误
[转] 解决方法: http://forum.springsource.org/showthread.php?85016-IllegalArgumentException-with-Applicati ...
- SpringMVC中对Controller使用AOP
转自http://usherlight.iteye.com/blog/1306111 正确配置spring aop,在controller中使用AOP 在controller中使用AOP的问题主要在于 ...
- spring中依赖注入与aop讲解
一.依赖注入 这个属于IOC依赖注入,也叫控制反转,IOC是说类的实例由容器产生,而不是我们用new的方式创建实例,控制端发生了改变所以叫控制反转. 1 2 3 4 5 6 7 8 9 10 11 1 ...
随机推荐
- SQL 用中文的拼音和笔画排序
SQL 用中文的拼音和笔画排序 城市按拼音排序: SELECT chineseName FROM [表名] order by chinesename collate Chinese_PRC_CS_ ...
- PHP学习笔记二十九【接口】
<?php //定义接口 //接口可以定义属性,但必须是常量而且是public //接口的所有方法必须是public interface Iusb{ public function start( ...
- 通过js引入当前所需要的js,css等
在我们web前端页面中经常会用到许多外部的js,css文件,那么问题来了,怎样才能一次性引入? 通过js便可实现 走码: //创建一个init.js文件 var jsUrls = "js/j ...
- 第二章IPC——IPC与开启多进程
问题 一.IPC ①.什么是IPC ②.为什么要有IPC 二.多进程 ①.如何开启多进程 ②.系统如何创建多进程 三.多进程引发的问题 问:私有进程(利用":+进程名")能否共 ...
- shell之变量替换:临时替换
${FILE:-word} 若变量为空,给变量FILE添加一个临时默认值word,FILE本身值并不变化eg: FILE1=${FILE:-word} 若FILE为空,则赋予FILE1值word $ ...
- Qt中事件分发源代码剖析(一共8个步骤,顺序非常清楚:全局的事件过滤器,再传递给目标对象的事件过滤器,最终传递给目标对象)
Qt中事件分发源代码剖析 Qt中事件传递顺序: 在一个应该程序中,会进入一个事件循环,接受系统产生的事件,并且进行分发,这些都是在exec中进行的.下面举例说明: 1)首先看看下面一段示例代码: in ...
- StreamReader与StreamWriter
StreamReader实现了抽象基类TextReader类,而StreamWriter实现了抽象基类TextWriter.分别用于对流的读取与写入. 先从StreamReader说起 一.构造方法 ...
- python 递归展开嵌套的序列(生成器用法)
任何使用yield语句的函数都称为生成器.调用生成器函数将创建一个对象,该对象通过连续调用next()方法(在python3中是__next__())生成结果序列. next()调用使生成器函数一直运 ...
- VC工程中的.rc文件和.rc2文件的区别
rc和rc2都是资源文件,包含了应用程序中用到的所有的资源. 两者不同在于:rc文件中的资源可以直接在VC集成环境中以可视化的方法进行编辑和修改; 而rc2中的资源不能在VC的集成环境下直接进行编辑和 ...
- cf471B MUH and Important Things
B. MUH and Important Things time limit per test 1 second memory limit per test 256 megabytes input s ...