在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 ...
随机推荐
- Silverlight Application 无法调用js的方法
今天下午做项目的时候遇到了这个错误 找了很多原因没找出,在蛋疼之际找出了问题, Silverlight调js方法的时候 js方法里面不能出现一点问题,如果有一点问题就会报这个错误.
- redis学习研究--Redis作者谈Redis应用场景
毫无疑问,Redis开创了一种新的数据存储思路,使用Redis,我们不用在面对功能单调的数据库时,把精力放在如何把大象放进冰箱这样的问题上,而是利用Redis灵活多变的数据结构和数据操作,为不同的大象 ...
- 两个string数组对应比较
最近做的array string类型对比.这个可能比较复杂,用的是linq 是请教别人的,我在这里记录一下 jquery 方法里面的数组 function arrtxt() { var arrt= [ ...
- java poi 合并单元格后边框问题
在项目中用poi合并单元格,但发现边框会有不显示的问题. 在网上搜集了答案,来记录一下. 解决方法: 将每个没用到的单元格都设空值. 例如: HSSFCell cell = row.createCel ...
- 学习PrintWriter类 .
java.io包 1)首先先知道它的八种构造方法,但怎么记住这八种呢?我们都知道PrintWriter是一种过滤流,也叫处理流.也就是能对字节流和字符流进行处理,所以它会有: PrintWriter( ...
- LaTeX使用titlesec宏包改变章节编号形式的方法
1.titleformat宏包命令详解 LaTeX中可以用titlesec宏包中的titleformat命令来改变标题形式: 导入宏包: \usepackage{titlesec} 改变标题的代码如下 ...
- 连接postgresql数据库
初装postgresql数据库会产生默认的数据库用户postgres和同名的数据库.但是我的linux用户是jm,两者不一致,连不上数据库.于是先把linux用户切换为postgres.(数据库会为l ...
- sql helper
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.C ...
- wordpress教程之the_author_meta()显示用户的信息
描述 模板标签函数the_author_meta可以显示用户数据.如果该函数在文章主循环(Loop)中,则不必指定作者的ID值,标签所显示的就是当前文章作者的内容.如果在主循环(Loop)外,则需要指 ...
- 垃圾回收GC——JVM之七
垃圾回收是个复杂的过程: 请以此阅读下列文章: 垃圾回收1:http://blog.csdn.net/sun305355024sun/article/details/41394729 垃圾回收2:ht ...