关于spring.net的面向切面编程 (Aspect Oriented Programming with Spring.NET)-简介

本文翻译自Spring.NET官方文档Version 1.3.2

受限于个人知识水平,有些地方翻译可能不准确,但是我还是希望我的这些微薄的努力能为他人提供帮助。

侵删。

简介

Aspect-Oriented Programming (AOP)通过一些其他的对程序结构的思考来完善OOP。OO将应用程序分成一系列继承关系的对象,而AOP将编程划分成不同的切面(aspects),或者关注点(concerns)。这就使得类似于模块化的事务管理关注点可以横切很多的对象(这种关注点也叫做横切关注点(crosscut concerns)。

AOP framework 是Spring.NET的一个关键的组件。由于Spring.NET的Ioc容器并没有依赖AOP——也就是说你如果不想使用AOP的话你可以不必要使用——虽然AOP提供强大的中间件服务,能使spring.NET的Ioc容器功能更加完善。

AOP在Spring.NET中:

  • 提供了声明式企业级服务(declarative enterprise services),特别是作为COM+声明式服务(COM+ declarative services)的替代品。一种典型的例子就是声明式事务管理(declarative transaction management),即一种Spring.NET内部的事务抽象(transaction abstraction)。这个功能将计划在之后的Spring.NET版本中加入。
  • 使用户可以应用他们自定义的切面,通过AOP来完善OOP。

因此你可以把Spring.NET AOP看作一个替代COM+来实现声明式事务管理(declarative transaction management) 的技术,或者一种依赖Spring.NET AOP 框架来应用用户自定义的切面的实现。

AOP的一些概念

我们从定义一些核心的AOP概念开始。这些名词并不是Spring.NET专有的。不幸的是,AOP的术语并不一看上去那么浅显易懂。然而如果Spring.NET使用自己的名词会更加让人困惑。

  • 切面(aspect):一个关注点的模块化,这个关注点在执行时候可能横切多个实体对象。在企业级应用中,事务管理就是一个很好的横切例子。 切面在Spring.NET中被当作通知器(Advisors)或者拦截器(interceptors)。
  • 连接点(Joinpoint):程序执行时候的一些时间点,例如调用一个方法的时候,或者一个特定的异常被抛出的时候。
  • 通知(Advice):AOP框架在特定的连接点执行的动作。不同类型的通知包括”环绕“,”前置“和”抛出异常后“。通知的类型将会在下文中讨论。很多AOP框架,包括Spring.NET,模型和通知都被当作拦截器”interceptor“,这些框架就围绕着连接点去维护这一系列拦截器。
  • 切入点(Pointcut):当一个通知被激活的时候的一系列连接点。一个AOP框架必须可以让开发者去定义特定的切入点,例如使用正则表达式。
  • 引入(Introduction):向一个通知类中加入方法或者属性。Spring.NET允许你将新的接口引入到任何的通知实体中。例如,你可以使用通知让任何实体去实现IAuditable接口来使追踪实体状态的变化更加简便。
  • 目标(Target object):包含连接点的实体,可以是被通知或者代理对象。
  • AOP代理(AOP proxy):AOP框架生成的实体,包括通知。在Spring.NET中,一个AOP代理是一个动态代理,在进行时通过IL中间语言生成。
  • 织入(Weaving):将多个切面组合成一个通知实体。这个可以在编译时(例如使用 GripperLoom.NET compiler)或者在运行时完成。Spring.NET是在运行时完成织入。

不同的通知类型包括:

  • 环绕通知(Around advice):通知围绕一个连接点,例如一个方法被调用。这个是一种最强大的通知类型。环绕通知会在方法调用前后完成自定义的行为,它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。
  • 前置通知(Before advice):通知在连接点之前执行,但是并没有能力阻止接下来的程序执行(除非程序抛出异常)。
  • 抛出异常后通知(Throws advice):通知在一个方法抛出异常之后执行。Spring.NET提供了强类型的抛出异常后通知,所以你可以截获你需要的异常而不一定去处理这些异常。
  • 返回后通知(After returning advice):通知在连接点正常经过后执行,例如一个方法正常返回没有抛出异常。

Spring.NET提供了多种通知类型。我们建议你使用最适当的通知类型来实现相应的行为。例如,如果你只需要在方法返回后更新缓存,你最好选择使用返回后通知而不是环绕通知,虽然环绕通知也可以达到相同的效果。使用最适当的通知类型可以使你的程序模型更加准确简单以减少潜在的错误。例如,当你在IMethodInvocation接口中不需要调用proceed()方法,你就不会调用失败。

在AOP中切入点是一个重要的概念,这个概念将AOP和一些提供切面的早期技术区分开。这些切入点独立针对通知OO的继承结构。例如,一个环绕通知提供了声明式事务管理可以被应用在多个对象的一系列方法中。因此切入点构成了了AOP的结构基础。

Spring.NET AOP的特点

Spring.NET的AOP基于纯C#。我们并不需要什么特殊的编译过程——所有的织入都在进行时完成。Spring.NET的AOP模块不需要控制或者修正程序集加载,也不依赖非托管的API,因此在任何CLR环境下都适用。

Spring.NET现在支持方法调用的拦截。但是属性的拦截并不被支持,尽管增加这样的支持并不会改动 Spring.NET AOP的 核心API。一个有争议的观点认为对属性的拦截会打破OO的封装性,因此我们认为添加这种拦截的支持并不是一个理智的行为。

Spring.NET提供了一些类来支持切入点和不同的通知类型。Spring.NET为实体展现一个切面而使用通知器,包括一个通知和一个面向特定连接点的切入点。

不同的通知类型都实现IMethodInterceptor接口(从AOP Alliance interception API);并且通知接口在 Spring.Aop 命名空间中定义。所有的通知必须实现 AopAlliance.Aop.IAdvice接口。通知实现 IMethodInterceptor ; IThrowsAdvice; IBeforeAdvice;和IAfterReturningAdvice接口。我们会在下文讨论通知类型。

Spring.NET提供了一个.NET版本的Java接口,这个接口被定义在AOP Alliance中。环绕通知必须实现AOP Alliance的 AopAlliance.Interceptr.IMethodInterceptor接口。同时在Java中也有对AOP alliance的广泛支持。 Spring.NET 是目前唯一的使用这些接口的.NET AOP框架。在短期内,这样能为那些开发者,无论是.NET还是Java,提供一个统一的模型,在长期来看,我们希望看到更多的.NET项目使用AOP Aliance的接口。

Spring.NET AOP目的不是为了提供一个和 AspectJ一样功能的AOP实现,而是为了提供一个会在.NET开发中遇到问题的解决方案。因此,我们会看见Spring.NET AOP会和Spring.NET IoC容器结合使用的情况。AOP通知会通过一般的实体定义语法( normal object definition syntax )来确定(实现了强大的“自动代理”机制),而通知和切入点都通过Spring.NET Ioc去管理。

Spring.NET中的AOP代理

Spring.NET在运行时生成AOP代理,这些代理类的IL代码是通过System.Reflection.Emit命名空间下面的类产生的。这个可以让这些代理非常高效并且不需要在继承结构上面引入其他的限制。

其他通常在.NET上面实现AOP代理的做法是使用ContextBoundObject和.NET remoting架构来作为拦截机制。我们不是特别喜欢ContextBoundObject的方式,因为这种方式需要代理类去直接或者间接地去继承ContextBoundObject 。在我们看来这是一种不必要的限制,这种限制会影响你模型的设计也同时把AOP关联到你无法直接控制的第三方类上。由于上下文切换和.NET remoting架构的性能开销,上下文代理也同样比IL产生的代理更加低效。

Spring.NET AOP代理也非常智能,这个是因为代理配置在代理生成的时候就很明确,因此生成的代理在调用的时候就可以被优化:仅仅在必要的时候被通过反射机制调用(例如当有一个目标方法产生了通知的时候)。在其他的情况下,方法会被直接调用,这样就避免了反射的使用带来的性能问题。

最后,Spring.NET AOP永远不会返回一个目标实体的原始引用(raw reference)。不管何时一个目标方法返回一个对目标实体的原始引用(例如“return this”),AOP代理都会识别得到然后将返回值替换成它自身的引用。

现在对AOP代理生成的实现是使用了对象组合(object composition)去委托一个目标对象的代理的调用,类似于一个经典的装饰器模式。这就意味着被代理的类必须实现一个或者多个接口,这就是我们的看法:这样做不仅仅比继承ContextBoundObject引入更少其他会产生干扰的东西,同时也是一个在构建常见的AOP代理目标服务类中应该遵守的好例子。

将来我们会使用继承来实现代理,这样做就可以允许你在代理的时候不需要通过接口,同时也解决了一些遗留的在使用基于对象组合的代理(composition-based proxies)无法解决的原始引用问题。

posted @ 2016-08-26 14:59  balavatasky  阅读(205)  评论(0编辑  收藏  举报