面向切面编程(AOP)通过提供另外一种思考程序结构的途经来弥补面向对象编程(OOP)的不足。在OOP中模块化的关键单元是类(classes),而在AOP中模块化的单元则是切面。切面能对关注点进行模块化,例如横切多个类型和对象的事务管理。(在AOP术语中通常称作横切(crosscutting)关注点。)AOP框架是Spring的一个重要组成部分。但是Spring IoC容器并不依赖于AOP,这意味着你有权利选择是否使用AOP,AOP做为Spring IoC容器的一个补充,使它成为一个强大的中间件解决方案。
AOP在Spring Framework中的作用提供声明式企业服务,特别是为了替代EJB声明式服务。最重要的服务是声明性事务管理。允许用户实现自定义切面,用AOP来完善OOP的使用。在业务系统中,总会在一些与业务逻辑无关服务逻辑(如:日志记录,安全验证,事物管理等)。
在传统的编程方式中,这些服务逻辑的代码总是会渗透到业务逻辑的代码中,使得服务逻辑和业务逻辑完成完全混杂在一起,这样会使业务系统变的更加复杂,根本不适用于当前大型业务系统的开打。如图:
图为:传统编程方式下服务逻辑与业务逻辑交织
引入AOP后,把这些服务逻辑收集起来,设计成各个独立可重用的切面,在需要该服务的业务逻辑之上织如即可。这样,这些服务逻辑就灵活地应用到业务系统中,在调用业务逻辑代码时并不关心他们的存在。如图。
图为:引入AOP后服务逻辑和业务逻辑分离
要理解AOP,必须先理解好AOP的一下几个术语:
l 切面(Aspect):
一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。在Spring AOP中,切面可以使用基于模式)或者基于@Aspect注解的方式来实现。
l 连接点(Joinpoint):
在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在Spring AOP中,一个连接点总是表示一个方法的执行。
l 通知(Advice):
在切面的某个特定的连接点上执行的动作。其中包括了“around”、“before”和“after”等不同类型的通知(通知的类型将在后面部分进行讨论)。许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。
l 切入点(Pointcut):
匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。
l 引入(Introduction):
用来给一个类型声明额外的方法或属性(也被称为连接类型声明(inter-type declaration))。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用引入来使一个bean实现IsModified接口,以便简化缓存机制。
l 目标对象(Target Object):
被一个或者多个切面所通知的对象。也被称做被通知(advised)对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。
l AOP代理(AOP Proxy):
AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
l 织入(Weaving):
把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
声明:
通知类型:
前置通知(Before advice):在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。
后置通知(After returning advice):在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
异常通知(After throwing advice):在方法抛出异常退出时执行的通知。
最终通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
环绕通知(Around Advice):包围一个连接点的通知,如方法调用。这是***大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。
通过切入点匹配连接点的概念是AOP的关键,这使得AOP不同于其它仅仅提供拦截功能的旧技术。 切入点使得通知可以独立对应到面向对象的层次结构中。例如,一个提供声明式事务管理 的环绕通知可以被应用到一组横跨多个对象的方法上(例如服务层的所有业务操作)。
Spring AOP 实现
在Spring2.5.6中,常用的AOP实现的两种方法:
***种,是基于xml配置文件方式的实现。
第二种,是基于注解方式实现的。
那么Spring AOP中使用@AspectJ(注解)还是XML?他们没有没有个子的优缺点?
如果你不是运行 在Java 5上,XML风格是***选择。对于使用Java 5的项目,需要考虑多方面的折衷。
XML风格对现有的Spring用户来说更加习惯。它可以使用在任何Java级别中 (参考连接点表达式内部的命名连接点,虽然它也需要Java 5+) 并且通过纯粹的POJO来支持。当使用AOP作为工具来配置企业服务时XML会是一个很好的选择。 (一个好的例子是当你认为连接点表达式是你的配置中的一部分时,你可能想单独更改它) 对于XML风格,从你的配置中可以清晰的表明在系统中存在那些切面。
XML风格有两个缺点。
***是 它不能完全将需求实现的地方封装到一个位置。 DRY原则中说系统中的每一项知识都必须具有单一、无歧义、权威的表示。 当使用XML风格时,如何实现一个需求的知识被分割到支撑类的声明中以及XML配置文件中。 当使用@AspectJ风格时就只有一个单独的模块 -切面- 信息被封装了起来。
第二是 XML风格同@AspectJ风格所能表达的内容相比有更多的限制:仅仅支持"singleton"切面实例模型, 并且不能在XML中组合命名连接点的声明。例如,在@AspectJ风格中我们可以编写如下的内容:
Xml代码
- @Pointcut(execution(* get*()))
- public void propertyAccess() {}
- @Pointcut(execution(org.xyz.Account+ *(..))
- public void operationReturningAnAccount() {}
- @Pointcut(propertyAccess() && operationReturningAnAccount())
- public void accountPropertyAccess() {}
在XML风格中能声明开头的两个连接点:
Xml代码
expression="execution(org.xyz.Account+ *(..))"/>
expression="execution(org.xyz.Account+ *(..))"/>
但是不能通过组合这些来定义accountPropertyAccess连接点
@AspectJ风格支持其它的实例模型以及更丰富的连接点组合。它具有将切面保持为一个模块单元的优点。 还有一个优点就是@AspectJ切面能被Spring AOP和AspectJ两者都理解 - 所以如果稍后你认为你需要AspectJ的能力去实现附加的需求,那么你非常容易迁移到基于AspectJ的途径。 总而言之,我们更喜欢@AspectJ风格只要你有切面去做超出简单的“配置”企业服务之外的事情。
结束语
我们完全可以混合使用以下几种风格的切面定义:使用自动代理的@AspectJ风格的切面, schema-defined
【编辑推荐】