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

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

侵删。

让我们看看 Spring.NET 如何处理通知。

通知的生命周期

Spring.NET通知可以被所有的被通知对象共享,或者针对不同的通知对象构造不同的通知。这个要看通知类型是per-class还是per-instance。

per-class通知是最经常用到的通知类型,这种通知类型适用于泛型通知,例如事务通知器。它们不依赖于被代理对象的现在状态或者一个其他新的状态,它们只关心方法和输入参数。

per-instance通知类型适用于引入以支持混入(mixins)。在这种情况下,通知会为被代理对象添加状态。

在AOP代理中也可以混合使用共享通知和per-instance通知。

通知类型

Spring.NET提供了多种可以直接使用的通知类型,并且可以支持多种其他通知类型扩展。让我们看一些基础的概念和标准的通知类型。

环绕通知(Interception Around Advice)

Spring.NET最基本的通知类型是环绕通知。

Spring.NET的环绕通知类型接口遵守AOP Alliance接口规范。环绕通知实现了以下接口:

 public interface IMethodInterceptor : IInterceptor
{
object Invoke(IMethodInvocation invocation);
}

IMethodInvocation 传到Invoke()的参数展示了需要被调用的方法;目标连接点;AOP代理和方法的其他参数。Invoke()方法应该返回调用的结果:连接点的返回值。

一个简单的IMethodInterceptor 实现如下:

 public class DebugInterceptor : IMethodInterceptor {
public object Invoke(IMethodInvocation invocation) {
Console.WriteLine("Before: invocation=[{0}]", invocation);
object rval = invocation.Proceed();
Console.WriteLine("Invocation returned");
return rval;
}
}

需要注意的是MethodInvocation的Proceed()方法的调用。这个方法把拦截器链(interceptor chain )朝着连接点继续下去。大多的拦截器都会调用这个方法,然后将这个方法的返回值返回。然而,一个IMethodInterceptor实例,就像其他的环绕通知一样,会返回一个不同的值或者抛出异常而不是调用Porceed()方法。但无论怎样,你肯定不会这么做。

前置通知(Before advice)

一个更加简单的通知类型是前置通知。这种通知类型不需要实现IMethodInvocation接口,因为它只会在调用某个方法之前被调用。

前置通知的主要优点在于它不需要调用Proceed()方法,因此不可能影响拦截而使整个拦截链断裂。

IMethodBeforeAdvice 接口定义如下:

 public interface IMethodBeforeAdvice : IBeforeAdvice
{
void Before(MethodInfo method, object[] args, object target);
}

需要主要的是,前置通知返回值类型是void。前置通知可以在关键点执行之前插入自定义的行为,但是不能改变返回值。如果一个前置通知抛出一个错误,它会忽视整个拦截链的其他错误。错误将会通过拦截链传递上去。如果它是unchecked的或者是在被调用方法的签名中,它会被直接传递给用户,不然它就会被AOP代理包装(wrapped)成unchecked错误。

以下是一个Spring.NET中的前置通知的例子,这个前置通知记录了所有正常返回的方法的数量:

 public class CountingBeforeAdvice : IMethodBeforeAdvice {
private int count; public void Before(MethodInfo method, object[] args, object target) {
++count;
}
public int Count {
get { return count; }
}
}

前置通知可以应用在所有的切入点中。

抛出异常后通知(Throws advice)

异常后通知在关键点抛出异常之后被调用。Spring.Aop.IThrowsAdvice接口不包含任何方法:它是一个标签接口(tag interface )表明实体实现一个或者多个异常后通知方法。这些异常后通知方法必须是这样组成:

 AfterThrowing([MethodInfo method, Object[] args, Object target], Exception subclass

异常后通知方法必须被叫做“'AfterThrowing'”。它的返回值会被Spring.NET AOP framework忽视,所以返回值一般是void。关于方法的传入参数,只有最后一个参数是必要的。因此,这个方法只有一个或者四个传入参数,这要根据通知方法是否对方法,方法参数和目标对象这几个因素感兴趣。

下面的方法片段展示了一些异常后通知的例子:

这个方法会被会在RemotingException 异常抛出后被调用(包括子类):

 public class RemoteThrowsAdvice : IThrowsAdvice {
public void AfterThrowing(RemotingException ex) {
// Do something with remoting exception
}
}

下面的通知会在SqlException 异常抛出后背调用。和上面的通知不一样,它有4个传入参数,所以它要获得被通知方法,方法参数和目标实体等信息:

 public class SqlExceptionThrowsAdviceWithArguments : IThrowsAdvice {
public void AfterThrowing(MethodInfo method, object[] args, object target, SqlException ex) {
// Do something will all arguments
}
}

最后的这个例子展示了这两个方法如何在一个类中实现,他们处理了RemotingException 和SqlException两个异常。任意数量的异常后通知都可以在一个类中实现,就像下面的例子一样:

 public class CombinedThrowsAdvice : IThrowsAdvice {
public void AfterThrowing(RemotingException ex) {
// Do something with remoting exception
}
public void AfterThrowing(MethodInfo method, object[] args, object target, SqlException ex) {
// Do something will all arguments
}
}

最后,有必要提一下,异常后通知仅仅应用在实际上抛出的异常。这个是什么意思呢?这个意思就是如果你在之前已经定义了处理RemotingException的通知,可调用方法'AfterThrowing'将仅在抛出异常是RemotingException 的时候被调用。如果RemotingException异常被抛出,而这个异常被包裹在其他异常中间并且在其他异常之前抛出,这时处理RemotingException 的异常通知就不会被调用。考虑一个处理RemotingException异常后通知的业务方法,如果在方法调用期间这个业务方法抛出一个RemotingException ,而RemotingException 是被包裹在一个业务里特定的BadConnectionException 之中(可以看以下的代码片段),这时异常后通知就不会响应RemotingException因为异常后通知只监测到BadConnectionException。事实上,被包裹在BadConnectionException 中的RemotingException 是不重要的。

 public void BusinessMethod()
{
try
{
// do some business operation...
}
catch (RemotingException ex)
{
throw new BadConnectionException("Couldn't connect.", ex);
}

返回后通知(After returning advice)

Spring.NET中的返回后通知必须实现Spring.Aop.IAfterReturningAdvice接口,如下:

 public interface IAfterReturningAdvice : IAdvice
{
void AfterReturning(object returnValue, MethodBase method, object[] args, object target);
}

返回后通知可以获得返回值(但是不能改变它),调用方法,方法的参数和目标。

下面的返回后通知记录了所有被成功调用,并且没有抛出任何异常的方法数量:

 public class CountingAfterReturningAdvice : IAfterReturningAdvice {
private int count;
public void AfterReturning(object returnValue, MethodBase m, object[] args, object target) {
++count;
}
public int Count {
get { return count; }
}
}

这个通知没有改变执行过程。如果它抛出了异常,拦截链会抛出异常而不是返回结果。

通知顺序

当多个通知存在于同一个连接点上,优先顺序由通知实现的IOrdered接口决定或者在通知器中指定。

引入通知(Introduction advice)

Spring.NET 允许你向一个被通知的类添加新的方法和属性。当你想添加的功能是一个横切关注点,并且你想把这个功能引入到这个类既定的继承结构中。例如你可能想在你代码中把实体注入到一个接口中。引入也是多重继承的一种方法。

引入就是就是通过使用一个一般的实现IAdvice接口声明方式实现IAdvice。

作为一个简单的例子,考虑一个IAuditable接口,这个接口功能是记录一个实体最后修改的时间:

 public interface IAuditable : IAdvice
{
DateTime LastModifiedDate
{
get;
set;
}
}

IAdvice接口细节如下:

 public interface IAdvice
{
}

可以通过实现ITargetAware来获得被通知的实体:

 public interface ITargetAware
{
IAopProxy TargetProxy
{
set;
}
}

IAopProxy包含了一个间接获得被通知实体的引用功能:

 public interface IAopProxy
{
object GetProxy();
}

一个简单的描述这个功能的类如下定义:

 public interface IAuditable : IAdvice, ITargetAware
{
DateTime LastModifiedDate
{
get;
set;
}
}

一个实现这个接口的类定义如下:

 public class AuditableMixin : IAuditable
{
private DateTime date;
private IAopProxy targetProxy;
public AuditableMixin()
{
date = new DateTime();
}
public DateTime LastModifiedDate
{
get { return date; }
set { date = value; }
}
public IAopProxy TargetProxy
{
set { targetProxy = value; }
}
}

引入通知并不和一个切入点关联,因为它是应用在类层面而不是模型层面。因此,引入通知使用他们自己的 IAdvisor接口子类,名叫 IIntroductionAdvisor,来定义这个引入通知可以应用的类型。

 public interface IIntroductionAdvisor : IAdvisor
{
ITypeFilter TypeFilter { get; } Type[] Interfaces { get; }
void ValidateInterfaces();
}

TypeFilter属性返回了这个引入通知应用的对象类型的过滤结果。

Interfaces属性返回被通知器引入的接口。

ValidateInterfaces()方法在内部用来查看被引入的接口是不是能够被引入通知实现。

Spring.NET提供了一个默认的接口实现(DefaultIntroductionAdvisor类),足够应付你需要使用引入通知的大多数场景。一个最简单的引入通知器的实现方式是继承DefaultIntroductionAdvisor,然后再构造函数中传入一个新实例。传入一个新实例十分重要,因为我们想要为每个被通知的实例都能使用这个混合类(mixin class)。

 public class AuditableAdvisor : DefaultIntroductionAdvisor
{
public AuditableAdvisor() : base(new AuditableMixin())
{
}
}

你也可以显示指定要引入的接口。可以通过查看SDK文档以获得更多信息。

要生成引入通知器,我们可以使用IAdvised.AddIntroduction()方法或者在XML配置文档中在ProxyFactoryObject下使用IntroductionNames属性节点,稍后会介绍这个方法。

和Java中Spring Framework的AOP实现不同,Spring.NET 的引入通知并不是一种特别的拦截器类型。这种方法的优点在于引入通知不是在拦截链中,这就可以进行一些性能优化。当一个方法不通过拦截器被调用的时候,我们就可以直接调用方法而不是都使用反射机制,无论这个方法是在目标对象中还是引入的一部分。也就是说引入的方法和实体的方法性能相同,这在向一个细粒度的实体引入方法的时候很有用。缺点在于如果这个混合的功能无法再在调用堆栈中访问到。这个问题会在将来的Spring.NET AOP中解决。

Spring.NET中通知器的API

在Spring.NET中,通知器是切面的模块化。通知器一般由一个通知和一个切入点组合构成。

除了引入通知之外,所有的通知器都可以和任何通知使用。Spring.Aop.Support.DefaultPointcutAdvisor类是一个最常用的通知器实现,可以和IMethodInterceptor, IBeforeAdvice 或者 IThrowsAdvice接口以及其他任意的切入点定义共同使用。

其他便捷的实现方式包括:在13.2.3.1.2节介绍的AttributeMatchMethodPointcutAdvisor ,基于特性的切入点。RegularExpressionMethodPointcutAdvisor ,基于正则表达式的切入点。

在Spring.NET中,我们可以在一个AOP代理中混入多种通知器和通知类型。例如,你可以在一个代理配置中使用环绕通知,抛出异常后通知和前置通知:Spring.NET 会自动生成需要的拦截链。

关于spring.net的面向切面编程 (Aspect Oriented Programming with Spring.NET)-通知(Advice)API的更多相关文章

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

    本文翻译自Spring.NET官方文档Version 1.3.2. 受限于个人知识水平,有些地方翻译可能不准确,但是我还是希望我的这些微薄的努力能为他人提供帮助. 侵删. 简介 Aspect-Orie ...

  2. 关于spring.net的面向切面编程 (Aspect Oriented Programming with Spring.NET)-使用工厂创建代理(Using the ProxyFactoryObject to create AOP proxies)

    本文翻译自Spring.NET官方文档Version 1.3.2. 受限于个人知识水平,有些地方翻译可能不准确,但是我还是希望我的这些微薄的努力能为他人提供帮助. 侵删. 如果你正在为你的业务模型使用 ...

  3. 关于spring.net的面向切面编程 (Aspect Oriented Programming with Spring.NET)-切入点(pointcut)API

    本文翻译自Spring.NET官方文档Version 1.3.2. 受限于个人知识水平,有些地方翻译可能不准确,但是我还是希望我的这些微薄的努力能为他人提供帮助. 侵删. 让我们看看 Spring.N ...

  4. 面向切面编程 ( Aspect Oriented Programming with Spring )

    Aspect Oriented Programming with Spring 1. 简介 AOP是与OOP不同的一种程序结构.在OOP编程中,模块的单位是class(类):然而,在AOP编程中模块的 ...

  5. 关于面向切面编程Aspect Oriented Programming(AOP)

    最近学到spring ,出来了一个新概念,面向切面编程,下面做个笔记,引自百度百科. Aspect Oriented Programming(AOP),面向切面编程,是一个比较热门的话题.AOP主要实 ...

  6. javascript 高阶函数 实现 AOP 面向切面编程 Aspect Oriented Programming

    AOP的主要作用是吧一些跟核心业务逻辑模块无关的功能 -日志统计, 安全控制, 异常处理- 抽离出来, 再通过"动态织入"的方式掺入业务逻辑模块中. 这里通过扩展Function. ...

  7. 程序员笔记|Spring IoC、面向切面编程、事务管理等Spring基本概念详解

    一.Spring IoC 1.1 重要概念 1)控制反转(Inversion of control) 控制反转是一种通过描述(在java中通过xml或者注解)并通过第三方去产生或获取特定对象的方式. ...

  8. Spring框架系列(4) - 深入浅出Spring核心之面向切面编程(AOP)

    在Spring基础 - Spring简单例子引入Spring的核心中向你展示了AOP的基础含义,同时以此发散了一些AOP相关知识点; 本节将在此基础上进一步解读AOP的含义以及AOP的使用方式.@pd ...

  9. Spring AOP:面向切面编程,AspectJ,是基于spring 的xml文件的方法

    导包等不在赘述: 建立一个接口:ArithmeticCalculator,没有实例化的方法: package com.atguigu.spring.aop.impl.panpan; public in ...

随机推荐

  1. linux socket c/s上传文件

    这是上传文件的一个示例,可以参照自行修改成下载或者其它功能. 在上传时,需要先将文件名传到服务器端,这是采用一个结构体,包含文件名及文件名长度(可以用于校验),防止文件名乱码. client #inc ...

  2. android 在自定义的listview(有刷新加载项)列表中,数据过少时不能铺满整个屏幕时,header和footer同时显示问题

    android  在自定义的listview(有刷新加载项)列表中,数据过少时,当刷新时,加载项也会显示,这是很头疼的一个问题,查阅了一些资料,总结了一个比较不错的方法: 原来代码: @Overrid ...

  3. 剑指offer:二维数组中的查找

    目录 题目 解题思路 具体代码 题目 题目链接 剑指offer:二维数组中的查找 题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺 ...

  4. URAL 1934 spfa算法

    D - Black Spot Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Subm ...

  5. MyBatis 基本演示

    主配置文件 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration P ...

  6. 【bzoj2879】[Noi2012]美食节 费用流+动态加边

    原文地址:http://www.cnblogs.com/GXZlegend 题目描述 CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他 ...

  7. codeforces 834 D. The Bakery

    codeforces 834 D. The Bakery(dp + 线段树优化) 题意: 给一个长度为n的序列分成k段,每段的值为这一段不同数字的个数,最大化划分k端的值 $n <= 35000 ...

  8. 圆盘自动机 cell

    圆盘自动机 cell 一个n-m圆盘自动机,包含n个排列成一圈的方格,它们按1至n编号.每个方格中有一个整数,范围[0,m-1] .圆盘会进行d操作,每次d操作会使得每个方格中的数同时变换,变换为与其 ...

  9. Angular(二)

    <!DOCTYPE html> <html lang="en" ng-app='myApp'> <head> <meta charset= ...

  10. Hibernate中用注解配置一对多双向关联和多对一单向关联

    Hibernate中用注解配置一对多双向关联和多对一单向关联 Hibernate提供了Hibernate Annotations扩展包,使用注解完成映射.在Hibernate3.3之前,需单独下载注解 ...