使用Spring进行面向切面编程AOP.docx

上传人:b****8 文档编号:10625046 上传时间:2023-02-21 格式:DOCX 页数:12 大小:22.62KB
下载 相关 举报
使用Spring进行面向切面编程AOP.docx_第1页
第1页 / 共12页
使用Spring进行面向切面编程AOP.docx_第2页
第2页 / 共12页
使用Spring进行面向切面编程AOP.docx_第3页
第3页 / 共12页
使用Spring进行面向切面编程AOP.docx_第4页
第4页 / 共12页
使用Spring进行面向切面编程AOP.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

使用Spring进行面向切面编程AOP.docx

《使用Spring进行面向切面编程AOP.docx》由会员分享,可在线阅读,更多相关《使用Spring进行面向切面编程AOP.docx(12页珍藏版)》请在冰豆网上搜索。

使用Spring进行面向切面编程AOP.docx

使用Spring进行面向切面编程AOP

使用Spring进行面向切面编程(AOP)

6.1.简介

面向切面编程(AOP)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(OOP)的不足。

除了类(classes)以外,AOP提供了切面。

切面对关注点进行模块化,例如横切多个类型和对象的事务管理。

(这些关注点术语通常称作横切(crosscutting)关注点。

Spring的一个关键的组件就是AOP框架。

尽管如此,SpringIoC容器并不依赖于AOP,这意味着你可以自由选择是否使用AOP,AOP提供强大的中间件解决方案,这使得SpringIoC容器更加完善。

Spring2.0AOP

Spring2.0引入了一种更加简单并且更强大的方式来自定义切面,用户可以选择使用基于模式(schema-based)的方式或者使用@AspectJ注解。

这两种风格都完全支持通知(Advice)类型和AspectJ的切入点语言,虽然实际上仍然使用SpringAOP进行织入(Weaving)。

本章主要讨论Spring2.0对基于模式和基于@AspectJ的AOP支持。

请查阅"AOP声明风格的选择"一节获取为你的应用选择适当的声明风格的建议。

Spring2.0完全保留了对Spring1.2的向下兼容性,下一章将讨论Spring1.2API所提供的底层的AOP支持。

Spring中所使用的AOP:

提供声明式企业服务,特别是为了替代EJB声明式服务。

最重要的服务是声明性事务管理(declarativetransactionmanagement),这个服务建立在Spring的抽象事务管理(transactionabstraction)之上。

允许用户实现自定义的切面,用AOP来完善OOP的使用。

这样你可以把SpringAOP看作是对Spring的一种增强,它使得Spring可以不需要EJB就能提供声明式事务管理;或者也可以使用SpringAOP框架的全部功能来实现自定义的切面。

本章首先介绍了AOP的概念,无论你打算采用哪种风格的切面声明,这个部分都值得你一读。

本章剩下的部分将着重于Spring2.0对AOP的支持;下一章提供了关于Spring1.2风格的AOP概述,也许你已经在其他书本,文章以及已有的应用程序中碰到过这种AOP风格。

如果你只打算使用通用的声明式服务或者预先打包的声明式中间件服务,例如缓冲池(pooling),那么你不必直接使用SpringAOP,而本章的大部分内容也可以直接跳过。

6.1.1.AOP概念

首先让我们从定义一些重要的AOP概念开始。

这些术语不是Spring特有的。

不幸的是,AOP术语并不是特别的直观;如果Spring使用自己的术语,将会变得更加令人困惑。

切面(Aspect):

一个关注点的模块化,这个关注点可能会横切多个对象。

事务管理是J2EE应用中一个关于横切关注点的很好的例子。

在SpringAOP中,切面可以使用通用类(基于模式的风格)或者在普通类中以@Aspect注解(@AspectJ风格)来实现。

连接点(Joinpoint):

在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。

在SpringAOP中,一个连接点总是代表一个方法的执行。

通过声明一个org.aspectj.lang.JoinPoint类型的参数可以使通知(Advice)的主体部分获得连接点信息。

通知(Advice):

在切面的某个特定的连接点(Joinpoint)上执行的动作。

通知有各种类型,其中包括“around”、“before”和“after”等通知。

通知的类型将在后面部分进行讨论。

许多AOP框架,包括Spring,都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。

切入点(Pointcut):

匹配连接点(Joinpoint)的断言。

通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。

切入点表达式如何和连接点匹配是AOP的核心:

Spring缺省使用AspectJ切入点语法。

引入(Introduction):

(也被称为内部类型声明(inter-typedeclaration))。

声明额外的方法或者某个类型的字段。

Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。

例如,你可以使用一个引入来使bean实现IsModified接口,以便简化缓存机制。

目标对象(TargetObject):

被一个或者多个切面(aspect)所通知(advise)的对象。

也有人把它叫做被通知(advised)对象。

既然SpringAOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。

AOP代理(AOPProxy):

AOP框架创建的对象,用来实现切面契约(aspectcontract)(包括通知方法执行等功能)。

在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

注意:

Spring2.0最新引入的基于模式(schema-based)风格和@AspectJ注解风格的切面声明,对于使用这些风格的用户来说,代理的创建是透明的。

织入(Weaving):

把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象。

这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。

Spring和其他纯JavaAOP框架一样,在运行时完成织入。

通知的类型:

前置通知(Beforeadvice):

在某连接点(joinpoint)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。

返回后通知(Afterreturningadvice):

在某连接点(joinpoint)正常完成后执行的通知:

例如,一个方法没有抛出任何异常,正常返回。

抛出异常后通知(Afterthrowingadvice):

在方法抛出异常退出时执行的通知。

后通知(After(finally)advice):

当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。

环绕通知(AroundAdvice):

包围一个连接点(joinpoint)的通知,如方法调用。

这是最强大的一种通知类型。

环绕通知可以在方法调用前后完成自定义的行为。

它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。

环绕通知是最常用的一种通知类型。

大部分基于拦截的AOP框架,例如Nanning和JBoss4,都只提供环绕通知。

跟AspectJ一样,Spring提供所有类型的通知,我们推荐你使用尽量简单的通知类型来实现需要的功能。

例如,如果你只是需要用一个方法的返回值来更新缓存,虽然使用环绕通知也能完成同样的事情,但是你最好使用Afterreturning通知而不是环绕通知。

用最合适的通知类型可以使得编程模型变得简单,并且能够避免很多潜在的错误。

比如,你不需要调用JoinPoint(用于AroundAdvice)的proceed()方法,就不会有调用的问题。

在Spring2.0中,所有的通知参数都是静态类型,因此你可以使用合适的类型(例如一个方法执行后的返回值类型)作为通知的参数而不是使用一个对象数组。

切入点(pointcut)和连接点(joinpoint)匹配的概念是AOP的关键,这使得AOP不同于其它仅仅提供拦截功能的旧技术。

切入点使得定位通知(advice)可独立于OO层次。

例如,一个提供声明式事务管理的around通知可以被应用到一组横跨多个对象中的方法上(例如服务层的所有业务操作)。

6.1.2.SpringAOP的功能和目标

SpringAOP用纯Java实现。

它不需要专门的编译过程。

SpringAOP不需要控制类装载器层次,因此它适用于J2EEweb容器或应用服务器。

Spring目前仅支持使用方法调用作为连接点(joinpoint)(在Springbean上通知方法的执行)。

虽然可以在不影响到SpringAOP核心API的情况下加入对成员变量拦截器支持,但Spring并没有实现成员变量拦截器。

如果你需要把对成员变量的访问和更新也作为通知的连接点,可以考虑其它语法的Java语言,例如AspectJ。

Spring实现AOP的方法跟其他的框架不同。

Spring并不是要尝试提供最完整的AOP实现(尽管SpringAOP有这个能力),相反的,它其实侧重于提供一种AOP实现和SpringIoC容器的整合,用于帮助解决在企业级开发中的常见问题。

因此,SpringAOP通常都和SpringIoC容器一起使用。

Aspect使用普通的bean定义语法(尽管Spring提供了强大的“自动代理(autoproxying)”功能):

与其他AOP实现相比这是一个显著的区别。

有些事使用SpringAOP是无法轻松或者高效的完成的,比如说通知一个细粒度的对象。

这种时候,使用AspectJ是最好的选择。

不过经验告诉我们:

于大多数在J2EE应用中遇到的问题,只要适合AOP来解决的,SpringAOP都没有问题,SpringAOP提供了一个非常好的解决方案。

SpringAOP从来没有打算通过提供一种全面的AOP解决方案来取代AspectJ。

我们相信无论是基于代理(proxy-based)的框架比如说Spring亦或是full-blown的框架比如说是AspectJ都是很有价值的,他们之间的关系应该是互补而不是竞争的关系。

Spring2.0可以无缝的整合SpringAOP,IoC和AspectJ,使得所有的AOP应用完全融入基于Spring的应用体系。

这样的集成不会影响SpringAOPAPI或者AOPAllianceAPI;SpringAOP保留了向下兼容性。

接下来的一章会详细讨论SpringAOPAPI。

6.1.3.Spring的AOP代理

Spring缺省使用J2SE动态代理(dynamicproxies)来作为AOP的代理。

这样任何接口都可以被代理。

Spring也支持使用CGLIB代理.对于需要代理类而不是代理接口的时候CGLIB代理是很有必要的。

如果一个业务对象并没有实现一个接口,默认就会使用CGLIB。

作为面向接口编程的最佳实践,业务对象通常都会实现一个或多个接口。

但也有可能会强制使用CGLIB,在这种情况(希望不常有)下,你可能需要通知一个没有在接口中声明的方法,或者需要传入一个代理对象给方法作为具体类型在Spring2.0之后,Spring可能会提供多种其他类型的AOP代理,包括了完整的生成类。

这不会影响到编程模型。

6.2.@AspectJ支持

"@AspectJ"使用了Java5的注解,可以将切面声明为普通的Java类。

AspectJ5发布的AspectJproject中引入了这种@AspectJ风格。

Spring2.0使用了和AspectJ5一样的注解,使用了AspectJ提供的一个库来做切点(pointcut)解析和匹配。

但是,AOP在运行时仍旧是纯的SpringAOP,并不依赖于AspectJ的编译器或者织入器(weaver)。

使用AspectJ的编译器或者织入器(weaver)的话就可以使用完整的AspectJ语言,我们将在Section6.8,“在Spring应用中使用AspectJ”中讨论这个问题。

6.2.1.启用@AspectJ支持

为了在Spring配置中使用@AspectJaspects,你必须首先启用Spring对基于@AspectJaspects的配置支持,自动代理(autoproxying)基于通知是否来自这些切面。

自动代理是指Spring会判断一个bean是否使用了一个或多个切面通知,并据此自动生成相应的代理以拦截其方法调用,并且确认通知是否如期进行。

通过在你的Spring的配置中引入下列元素来启用Spring对@AspectJ的支持:

我们假使你正在使用AppendixA,XMLSchema-basedconfiguration所描述的schema支持。

关于如何在aop的命名空间中引入这些标签,请参见SectionA.2.6,“Theaopschema”

如果你正在使用DTD,你仍旧可以通过在你的applicationcontext中添加如下定义来启用@AspectJ支持:

你需要在你的应用程序的classpath中引入两个AspectJ库:

aspectjweaver.jar和aspectjrt.jar。

这些库可以在AspectJ的安装包(1.5.1或者之后的版本)中的lib目录里找到,或者也可以在Spring依赖库的lib/aspectj目录下找到。

6.2.2.声明一个切面

在启用@AspectJ支持的情况下,在applicationcontext中定义的任意带有一个@Aspect切面(拥有@Aspect注解)的bean都将被Spring自动识别并用于配置在SpringAOP。

以下例子展示了为了完成一个不是非常有用的切面所需要的最小定义:

下面是在applicationcontext中的一个常见的bean定义,这个bean指向一个使用了@Aspect注解的bean类:

下面是NotVeryUsefulAspect类定义,使用了org.aspectj.lang.annotation.Aspect注解。

packageorg.xyz;

importorg.aspectj.lang.annotation.Aspect;@Aspect

publicclassNotVeryUsefulAspect{}

切面(用@Aspect注解的类)和其他类一样有方法和字段定义。

他们也可能包括切入点,通知和引入(inter-type)声明。

6.2.3.声明一个切入点(pointcut)

回想一下,切入点决定了连接点关注的内容,使得我们可以控制通知什么时候执行。

SpringAOP只支持Springbean方法执行连接点。

所以你可以把切入点看做是匹配Springbean上方法的执行。

一个切入点声明有两个部分:

一个包含名字和任意参数的签名,还有一个切入点表达式,该表达式决定了我们关注那个方法的执行。

在@AspectJ注解风格的AOP中,一个切入点签名通过一个普通的方法定义来提供,并且切入点表达式使用@Pointcut注解来表示(作为切入点签名的方法必须返回void类型)。

用一个例子会帮助我们区分切入点签名和切入点表达式之间的差别,下面的例子定义了一个切入点'anyOldTransfer',这个切入点将匹配任何名为"transfer"的方法的执行:

@Pointcut("execution(*transfer(..))")//thepointcutexpression

privatevoidanyOldTransfer(){}//thepointcutsignature

切入点表达式,也就是@Pointcut注解的值,是正规的AspectJ5切入点表达式。

如果你想要更多了解AspectJ的切入点语言,请参见AspectJ编程指南(如果要了解基于Java5的扩展请参阅AspectJ5开发手册)或者其他人写的关于AspectJ的书,例如Colyeret.al.著的《EclipseAspectJ》或者RamnivasLaddad著的《AspectJinAction》。

6.2.3.1.切入点指定者的支持

SpringAOP支持在切入点表达式中使用如下的AspectJ切入点指定者:

其他的切入点类型

完整的AspectJ切入点语言支持额外的切入点指定者,但是Spring不支持这个功能。

他们分别是call,initialization,preinitialization,staticinitialization,get,set,handler,adviceexecution,withincode,cflow,cflowbelow,if,@this和@withincode。

在SpringAOP中使用这些指定者将会导致抛出IllegalArgumentException异常。

SpringAOP支持的切入点指定者可能在将来的版本中得到扩展,不但支持更多的AspectJ切入点指定者(例如"if"),还会支持某些Spring特有的切入点指定者,比如"bean"(用于匹配bean的名字)。

execution-匹配方法执行的连接点,这是你将会用到的Spring的最主要的切入点指定者。

within-限定匹配特定类型的连接点(在使用SpringAOP的时候,在匹配的类型中定义的方法的执行)。

this-限定匹配特定的连接点(使用SpringAOP的时候方法的执行),其中beanreference(SpringAOP代理)是指定类型的实例。

target-限定匹配特定的连接点(使用SpringAOP的时候方法的执行),其中目标对象(被代理的appolicationobject)是指定类型的实例。

args-限定匹配特定的连接点(使用SpringAOP的时候方法的执行),其中参数是指定类型的实例。

@target-限定匹配特定的连接点(使用SpringAOP的时候方法的执行),其中执行的对象的类已经有指定类型的注解。

@args-限定匹配特定的连接点(使用SpringAOP的时候方法的执行),其中实际传入参数的运行时类型有指定类型的注解。

@within-限定匹配特定的连接点,其中连接点所在类型已指定注解(在使用SpringAOP的时候,所执行的方法所在类型已指定注解)。

@annotation-限定匹配特定的连接点(使用SpringAOP的时候方法的执行),其中连接点的主题有某种给定的注解。

因为SpringAOP限制了连接点必须是方法执行级别的,pointcutdesignators的讨论也给出了一个定义,这个定义和AspectJ的编程指南中的定义相比显得更加狭窄。

除此之外,AspectJ它本身有基于类型的语义,在执行的连接点'this'和'target'都是指同一个对象,也就是执行方法的对象。

SpringAOP是一个基于代理的系统,并且严格区分代理对象本身(对应于'this')和背后的目标对象(对应于'target')6.2.3.2.合并切入点表达式

切入点表达式可以使用using'&','||'和'!

'来合并.还可以通过名字来指向切入点表达式。

以下的例子展示了三种切入点表达式:

anyPublicOperation(在一个方法执行连接点代表了任意public方法的执行时匹配);inTrading(在一个代表了在交易模块中的任意的方法执行时匹配)和tradingOperation(在一个代表了在交易模块中的任意的公共方法执行时匹配)。

@Pointcut("execution(public**(..))")

privatevoidanyPublicOperation(){}@Pointcut("within(com.xyz.someapp.trading..*")

privatevoidinTrading(){}@Pointcut("anyPublicOperation()&&inTrading()")

privatevoidtradingOperation(){}

就上所示的,从更小的命名组件来构建更加复杂的切入点表达式是一种最佳实践。

当用名字来指定切入点时使用的是常见的Java成员可视性访问规则。

(比如说,你可以在同一类型中访问私有的切入点,在继承关系中访问受保护的切入点,可以在任意地方访问公共切入点。

成员可视性访问规则不影响到切入点的匹配。

6.2.3.3.共享常见的切入点(pointcut)定义

当开发企业级应用的时候,你通常会想要从几个切面来参考模块化的应用和特定操作的集合。

我们推荐定义一个“SystemArchitecture”切面来捕捉常见的切入点表达式。

一个典型的切面可能看起来像下面这样:

packagecom.xyz.someapp;importorg.aspectj.lang.annotation.Aspect;

importorg.aspectj.lang.annotation.Pointcut;@Aspect

publicclassSystemArchitecture{/**

*Ajoinpointisintheweblayerifthemethodisdefined

*inatypeinthecom.xyz.someapp.webpackageoranysub-package

*underthat.

*/

@Pointcut("within(com.xyz.someapp.web..*)")

publicvoidinWebLayer(){}/**

*Ajoinpointisintheservicelayerifthemethodisdefined

*inatypeinthecom.xyz.someapp.servicepackageoranysub-package

*underthat.

*/

@Pointcut("within(com.xyz.someapp.service..*)")

publicvoidinServiceLayer(){}/**

*Ajoinpointisinthedataaccesslayerifthemethodisdefined

*inatypeinthecom.xyz.someapp.daopackageoranysub-package

*underthat.

*/

@Pointcut("within(com.xyz.someapp.dao..*)")

publicvoidinDataAccessLayer(){}/**

*Abusinessserviceistheexecutionofanymethoddefinedonaservice

*interface.Thisdefinitionassumesthatinterfacesareplacedinthe

*"service"package,andthatimplementationtypesareinsub-packages.

*

*Ifyougroupserviceinterfacesbyfunctionalarea(forexample,

*inpackagescom.xyz.someapp.abc.serviceandcom.xyz.def.service)then

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 简历

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1