Spring AOP概念及切入点

来源:互联网 发布:js定义数组添加元素 编辑:程序博客网 时间:2024/06/06 14:09
 1,概念
面向切面编程是对程序OOP编程的另一种补充。OO将应用程序分解为对象层次,而AOP则将程序分解为各个方面或者关系。这就使得模块之间的关联能够跨多个对象进行处理,例如事务管理(在术语上被称为"横切")。
Spring中一个重要的组成部分就是AOP框架。如果Spring的IoC容器(BeanFactory和ApplicationContext)不依赖于AOP,也就意味着你不需要使用AOP。AOP为Spring IoC提供了一个非常能干的中间件解决方案。

AOP在Spring中的使用:
提供公布式企业级服务,特别是替代EJB公布式服务。最重要的是这种服务是公布式事务管理,建立在Spring的事务抽象上。
允许使用者实现自定义切面,对自己的OOP补充AOP。

因此你可以将Spring AOP看作一种可以允许Spring提供公布式事务管理的技术,而这种技术并不需要EJB;或者使用Spring的AOP框架来实现自己的切面。

1.1,AOP概念
让我们先了解一些AOP的概念和定义。这些术语并不是Spring特有。不幸的是,AOP术语并不显得直观。如果Spring使用它自己的术语,那么就更显得混淆。
Aspect(切面):一种跨多对象的横向模块化关系。J2EE应用程序中的事务管理就是一个很好的横切关系的例子。切面作为顾问和拦截应用于Spring中。
Joinpoint(接合点):程序执行过程中的点,就好象方法中的一个调用,或者一个特别的被抛出的异常。在Spring AOP中,一个接合点通常是方法调用。Spring并不明显地使用"接合点"作为术语;接合点信息可以通过MethodInvocation的参数穿过拦截器访问,相当于实现org.springframework.aop.Poinkcut接口。
Advice(通知):被AOP框架使用的特定接合点。不同的通知类型包括:"around","before"和"throws"通知。通知类型在下面介绍。许多AOP框架,包括Spring,通知模块就如同一个拦截器,维持一系列拦截器围绕着接合点。
Pointcut(切入点):当一个通知将被激活的时候,会指定一些结合点。一个AOP框架必须允许开发者指定切入点:例如使用正则表达式。
Introduction(引入):向一个通知类中添加方法或成员变量。Spring允许你向任何通知类中引入新接口。例如,你可能会使用一个引入,以使得任何实现IsModified接口的对象简单的缓存。
Target object(标签对象):包含接合点的对象。也被通知对象或者代理对象所引用。
AOP proxy(AOP代理):被AOP框架创建的对象,包括通知。在Spring中,一个AOP代理将会是一个JDK动态代理或者一个CGLIB代理。
Weaving(编织):聚合切面以生成一个通知对象。这可以在编译时(比如使用AspectJ编译器)或在运行时完成。Spring,如同其它纯Java AOP框架一样,在运行时完成编织。

通知类型介绍:
Around advice(围绕通知):通知围绕一个接合点(如一个方法调用)。这是最强有力的通知类型。围绕通知将会在方法调用之前或之后完成自定义行为。他们能够选择是否通过接合点,或者返回他们自己特定的值,甚至抛出异常。
Before advice(提前通知):通知在接合点之前执行,但是并却并没有能力阻止流程执行接合点(如果不抛出异常的话)。
Throws advice(抛出通知):如果一个方法抛出异常,通知将会被执行。Spring提供了健壮的类型以抛出通知,所以你可以按自己的喜好编写代码捕捉异常(和子类),而不需要通过Throwable或Exception。
After returning advice(返回通知):在接点正常完成之后,通知将会被执行。例如,如果一个方法返回却没有抛出异常。

围绕通知是通知中最普遍的类型。大多数基于AOP框架的拦截器,如Nanning Aspects,只提供了围绕通知。
而Spring,如同AspectJ,提供了所有通知类型。我们建议你使用,能够满足你需要的最低消耗通知类型。例如,如果你仅仅需要通过一个方法的返回值来更新缓存,那么你最好实现"返回通知",而不是"围绕通知",虽然围绕通知能够完成相同需求。使用高效的通知类型能够提供一个简单的程序模块,以减少潜在的错误。例如,你并不需要调用proceed()方法,该方法在基于围绕通知中的MethodInvocation使用。
切入点是AOP的关键,区别于AOP旧版本提供拦截器的技术。切入点能够被不同层次的OO通知触发。例如,一个围绕通知提供了公布式事务管理能够被应用于一系列方法,以生成若干对象。因此,切入点作为AOP的结构要素。

1.2,Spring AOP性能和目标
Spring AOP用于纯Java中。并不需要特殊编译过程。Spring AOP并不需要控制类装载层,并且因此适合应用于J2EE web容器,或者应用程序服务。
Spring现在支持方法调用拦截器。虽然增加成员变量拦截器并不会破坏Spring AOP APIs代码,但是成员变量拦截器并没有实现。(成员变量拦截器被证明是违背OO封装的,我们相信在应用程序中没有成员变量拦截器,并不是不好。如果你需要成员变量拦截器,可以考虑使用AspectJ)。
Spring提供了一些类以表现切入点和不同类型的通知。Spring使用术语:通知者,表示一个对象表现一个切面,包括一个通知和一个指定接合点的触发切入点。
不同的通知类型都是MethodInterceptor(来自the AOP Alliance interception API);而通知接口均在org.springframework.aop包中定义。所有通知必须实现org.apoalliance.aop.Advice接口。
Spring实现AOP Alliance拦截接口(http://www.sourceforge.net/projects/aopalliance),围绕通知必须实现AOP Alliance的org.apoalliance.intercpet.MethodInterceptor接口。实现这个接口能够在Spring中运行,或者任何其他AOP Alliance合适的执行。现在的JAC实现AOP Alliance接口,而Nanning和Dynaop可能在2004年早些时候。
Spring的方式不同于大多数其它AOP框架。Spring的目标不是提供一个完全的AOP实现(虽然Spring的AOP很能干)。Spring提供了一个AOP实现与Spring IoC之间的综合,以帮助解决在企业级中的大多数问题。
因此,例如,Spring的AOP普遍用于Spring IoC容器之间的连接。AOP通知是特定用于普通bean定义语法(虽然这允许强有力的"autoproxying"性能)。通知和接合点都是通过Spring IoC管理:相比于其他OAP实现,这是一个至关重要的区别。有些东西在Spring AOP中并不容易使用,效率也不高。例如高细粒度对象。在这种情况下,AspectJ是最好的选择。尽管以我们的经验,对于服从AOP的J2EE应用,Spring AOP提供了一个极好的解决方案。
Spring AOP将不会致力于提供一个完善全面的AOP解决方案,以和AspectJ或AspectWerkz竞争。我们相信那两种基于代理的框架像Spring与AspectJ都是有价值的,相互补充的,而不是竞争的关系。因此Spring1.1的一个主要目标,就是使得Spring AOP和IoC能够和AspectJ无缝隙地融合,以便使得基于Spring的应用程序结构能够使用所有的AOP功能,并有很好的协调性。这个融合并不会影响Spring AOP API或者AOP Alliance API,Spring AOP将会考虑对以前版本的兼容性。

1.3,Spring中的AOP代理
Spring默认使用J2SE的动态代理来处理AOP代理。这使得任何一个或一堆接口能够被代理。
Spring同样可以使用CGLIB代理。代理类比代理接口更有必要。CGLIB默认被用于业务对象不能够实现接口的情况。因为业务对象通常会显示一个或多个业务接口。
也可能会强迫使用CGLIB:我们会在下面讨论,并解释为什么你并不想这样做的原因。在Spring1.0之前,Spring可能提供额外的AOP代理类型,包括整个生成类。这并不会影响程序模型。

2,Spring中的切入点
让我们来看看Spring怎样操作至关重要的切入点概念。

2.1,概念
Spring的切入点模型能够使切入点不依赖通知类型而重复使用。可能同一个切入点会触发不同的通知。
org.springframework.aop.Pointcut接口是中心,用于特定的类和方法触发通知。完整的接口如下:

public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}

将Pointcut接口分离为两个部分,允许类的重复使用和方法的匹配,并能够高细粒度合成处理(就如同与另外一个方法的匹配器完成一个"联合")。
ClassFilter接口通过提供一些触发类,被用于约束切入点。如果matchs()方法总是返回true,那么所有的触发类将会被匹配:

public interface ClassFilter {
boolean matches(Class class);
}

MethodMatcher接口通常更重要,完整接口如下:

public interface MethodMatcher {
boolean matcher(Method m, Class targetClass) ;
boolean isRuntime();
boolean matches(Method m, Class targetClass, Object[] args);
}

matches(method, class)方法被用来测试是否这个切入点在触发类中会被给定的方法匹配。这种赋值当AOP代理被创建的时候可以被执行,以避免对每一个方法调用进行测试。对给予的方法,如果两个参数的匹配方法返回true,并且在使用MethodMatcher时,isRuntime()方法返回true,则三个参数的匹配方法将会在每个方法调用时被调用。这样就能够使得切入点,在触发通知被执行之前,直接通过参数传递给方法调用。
大多数MethodMatcher是静态的,意味着他们的isRuntime()返回false。这种情况下,三个参数的匹配方法将不会被调用。(如果可能,试着将切入点标为静态,使得当一个AOP代理被创建的时候,允许AOP框架缓存切入点所赋的值)。

2.2,切入点的运作
Spring支持三种切入点的运作:显著,联合和交集。
联合表示多个方法被任一切入点匹配。
拦截表示多个方法被两个切入点匹配。
联合通常更有用。
多个切入点可以使用org.springframework.aop.support.Pointcuts类通过静态方法组织起来,也可以使用同一个包下应用程序的其他特殊切入点类的子类。

2.3,方便地实现切入点
Spring提供了几种方便的方式实现切入点。其中一些方式是黑盒子式,另外的则需要继承指定的应用程序切入点类。

2.3.1,静态切入点
静态切入点基于方法及触发类,并且不需要考虑方法的参数。静态切入点对大多数使用来说,更好更高效。当方法被调用的时候,Spring可能只需要给静态切入点赋一次值。之后,当方法被再次调用的时候,不需要再次赋值。
让我们来看看Spring中集中静态切入点的实现:

2.3.1.1,正则表达式
正则表达式是一种显而易见的方式,以指定静态切入点。一些AOP框架(除了Spring)支持这种方式。org.springframework.apo.support.RegexpMethodPointcut是一种普遍的正则表达式切入点,使用Perl5正则表达式规则。
使用这个类,你可以提供一个正则字符串的list。如果其中任何一个正则字符串被匹配,那么切入点将会被赋值为true。


  
.*get.*
.*absquatulate
  


而一个RegexpMethodPointcut和RegexpMethodPointcutAdvisor的子类,可以使我们方便地引用一个通知(记住,通知是一个拦截器,可能是"提前通知",或者"抛出通知"等)。下面是一个将bean servers与切入点和通知者一起绑定的示例:


  


  
.*get.*
.*absqustulate
  


任何类型的通知都可以使用ReexpMethodPointcutAdvisor,而RegexpMethodPointcut类要求Jakarta ORO正则表达式包。

2.3.1.2,切入点属性控制
一种重要的静态切入点类型是metadata-driven(控制元数据)切入点。它使用元数据属性:典型的方式是多层元数据。

2.3.2,动态切入点
赋值动态切入点的消耗比静态切入点大。动态切入点作为方法的参数,也是静态信息。这意味着它们必须在方法每次被调用时赋值,且方法返回的结果不能够被缓存,因为参数的值是不停变化的。
典型的示例是控制流切入点:

2.3.2.1,控制流切入点
Spring控制流切入点在概念上与Aspect控制流切入点相似,但是Aspect控制流切入点更强大(Spring控制流切入点无法在一个切入点执行完之前指定另外一个切入点)。一个控制流切入点匹配当前的返回值。比如,可能当一个接入点在com.mycompany.web包中被一个方法或者SomeCaller类调用时,控制流切入点被触发。控制流切入点通过org.springframework.aop.support.ControlFlowPointcut类指定。
注意:控制流切入点比其它动态切入点消耗更大,在Java 1.4中,它是其他动态切入点的5被消耗,而在Java 1.3中,是10倍。

2.4 切入点的父类
Spring提供非常有用的切入点父类,以帮助你实现自己的切入点。
因为静态切入点更高效,你可以使用StaticMethodMatcherPointcut作为父类,如下示例。使用这个类需要实现一个抽象方法。
class TestStaticPointcut extents StaticMethodMatcherPointcut {
public boolean matches(Method m, Class targetClass) {
   // return true if custom criteria match
}
}
当然,Spring也提供了其它方式的父类。你可以在Spring 1.0RC2中使用任何通知类型的自定义切入点。

2.5,自定义切入点

因为Spring中切入点都是Java类,而不是语言特征(如在AspectJ中)。切入点的声明可以是静态的,或者动态的。尽管Spring没有提供黑盒子似的像AspectJ标号那样编码的灵活切入点表达式。但是自定义切入点在Spring中可以任意组合。


来自:http://hi.baidu.com/comtpucbrfbprxr/item/2de127da051d9410d90e44fd

原创粉丝点击