Spring AOP(一)——基础概念
前文的一些内容更多是针对Spring容器内部的一些特性的描述,接下来一个专题将描述Spring AOP的一些信息,配置细节等等。介绍面向切面编程(AOP)是一种新的针对程序结构的思路,它补足了面向对象编程(OOP)的一些细节上的不足。OOP的关键在于模块化概念,也就是Java中的class。而AOP关心的模块则是切面。切面关心的模块化主要是考虑的是诸如事物管理这类会跨越多个类型和对象的一些方面。(
前文的一些内容更多是针对Spring容器内部的一些特性的描述,接下来一个专题将描述Spring AOP的一些信息,配置细节等等。
介绍
面向切面编程(AOP)是一种新的针对程序结构的思路,它补足了面向对象编程(OOP)的一些细节上的不足。OOP的关键在于模块化概念,也就是Java中的class。而AOP关心的模块则是切面。切面关心的模块化主要是考虑的是诸如事物管理这类会跨越多个类型和对象的一些方面。(而这些类型对象通常是在多个不同业务层的。)
Spring的其中一个核心组件也就是AOP框架。而Spring IoC容器并不依赖AOP,也就是说,开发者如果不想使用AOP,就可以不使用AOP,AOP以一种中间件的方式补足了Spring IoC的功能。
Spring 2.0 AOP
Spring 2.0引入了一种既简单又有效的方式来支持定制切面,无论是使用基于契约(XML)的方式,还是基于@AspectJ
注解的方式。两种方式都是支持AOP的全部功能的。
之后的AOP系列会将针对两种方式分别讨论。Spring 2.0 AOP是完全兼容之前的Spring 1.2 AOP的。后续AOP系列也会讨论AOP的相关API。
AOP在Spring框架中起的作用主要有两点:
- 提供显式声明的企业服务,尤其是用来替换EJB服务的情况。其中最重要的服务就是诸如事务管理之类的服务。
- 允许用户来实现定制的切面,使用AOP来补足OOP的不足。
AOP 基本概念
首先,我们来看下AOP的核心概念以及相关术语。这些术语并不是Spring特有的。很可惜的是,AOP的术语不是很直观,尤其是在Spring使用它自己的术语的时候很令人疑惑。
- Aspect:一个横切多个类的模块化的服务。事物管理就是一个很好的例子,在企业应用当中,他会跨越很多的模块。在Spring的AOP中,切面是通过一般的类来实现(基于契约的方式)或者通过注解了
@Aspect
注解的一般类来实现(@AspectJ
方式)。 - JoinPoint:程序的一个执行点,比如一个方法的执行,或者是处理一个异常。在Spring AOP中,JoinPoint总是代表折方法的执行过程。
- Advice:JoinPoint过程中的一些动作。包括
around
,before
以及after
这几种类型的Advide。很多的AOP框架,包括Spring,都会将Advide作为一个interceptor(拦截器),保证在一个JoinPoint有一个拦截链。 - Pointcut:判定是否匹配JoinPoint,Advide是和Pointcut表达式关联的,只有在Pointcut匹配的时候,才会运行在JoinPoint上面(举例来说,匹配一个方法的名字)。JoinPoint的概念和Pointcut表达式匹配是AOP中最重要的概念,默认情况下,Spring使用AspectJ的Pointcut表达式语言。
- Introduction:声明额外的方法或者私有变量来代表一个类型。Spring AOP允许开发者来引入新的接口(以及对应的实现类)到切面对象上。举例来说,开发者可以通过使用Introduction,令Bean来实现
IsModified
接口,来简化缓存(Introduction作为AspectJ中的内在声明类型)。 - TargetObject:由切面切入的对象就是TargetObject。也称之为advised对象。因为Spring AOP是通过实时代理来实现的,这个对象将总是一个代理的对象。
- AOP proxy:为了实现切面约束而由AOP框架创建的对象就是AOP proxy。在Spring框架中,AOP的代理就是JDK动态代理或者CGLIB代理。
- Weaving:和应用其他类型连接的切面,或者是创建的Advice对象的过程。这一过程可以在编译期(使用AspectJ编译器),加载期,或者运行时完成。Spring AOP和其它的纯Java的AOP框架一样,是在运行时执行Weaving过程的。
Advice的类型:
- Before Advice: 在JoinPoint执行之前执行的Advice,但是不能够阻止JoinPoint的执行,除非抛出异常。
- After Returning Advice:在JoinPoint正常完成的情况下执行的Advice,简单来说,就是一个方法没有抛出异常。
- After Throwing Advice:在JoinPoint不正常完成的情况下执行的Advice,简来单说,就是方法抛出异常的时候执行的Advice。
- After(finally) Advice:无论一个JoinPoint执行正常还是非正常,都会执行的Advice。
- Around Advice:以一层调用包裹了JoinPoint的Advice,这是最常见,最有效的一种Advice。Around Adivce可以执行定制的行为,无论是在方法的执行之前还是执行之后。Around Advice也负责决定是否开始进入JoinPoint或者是执行方法其他方法来返回或者抛出异常。
Around Advice也是最泛化的一种Advice。但是Spring AOP中,像AspectJ都提供了很多不同类型的Advice,所以我们仍然建议开发者少使用Around Advice,而是使用最合适的Advice来实现特定的行为。举例来说,如果开发者只需要更新一个缓存来返回方法的值,那么实现一个Returning Advice就比Around Advice更好,尽管Around Advice也能够实现同样的功能。使用更为具体的Advice类型可以简化变成的模型,去除一些潜在的错误。举例来说,开发者在Around Advice上不需要在JoinPoint
上调用proceed()
方法,一旦调用就可能产生错误。
在Spring 2.0的版本中,所有的参数都是静态的定义的,所以当开发者使用Advice的参数的时候,可以使用适当的类型而不是Object
的数组。
JoinPoint的执行和PointCut的匹配概念,是AOP区别于老的的拦截技术的关键。PointCut使得Advice可以独立于面向对象层次来执行。举例来说,一个提供显式的事务管理的Around Advice可以用于多个对象,跨越多个不同的服务层级而工作。
Spring AOP的能力和目的
Spring AOP是纯Java实现的。不需要其他特殊的处理。Spring AOP也不需要控制类加载的层次,所以在应用服务器或者是Servlet容器中应用都很方便。
Spring AOP 当前只支持方法执行的JoinPoint(基于Spring Bean方法的Advice执行),实例变量的拦截是不支持的,尽管支持这个功能也不会破坏掉Spring AOP核心的API。如果开发者需要针对实例变量的访问和更新JoinPoint的话,可以考虑使用AspectJ语言。
Spring AOP框架和其他的AOP框架细节上还是有些不同的。Spring的目的不是为了支持最完整的AOP实现(尽管Spring AOP功能很完善),Spring AOP更多关心的是提供AOP和Spring IoC的集成,以便能够解决企业级应用的常见问题。
比如,Spring框架的AOP功能通常都是结合Spring IoC容器来使用的。切面的配置,也基本都是使用Bean定义的语法(当然Spring AOP也支持强大的自动代理的能力),这就是和其它AOP实现的一个最大的不同。当然,使用Spring AOP也会有一些限制,比如Advice一些细粒度的对象(比如域对象之类的)很难:如果AOP面对的常见问题多数是这种情况的话,使用AspectJ可能是更好的选择。当然,Spring团队的经验是,Spring AOP对绝大多数的企业应用场景,都是十分适用的。
Spring AOP不会努力去实现所有AspectJ的功能以提供完整的AOP解决方案。我们相信无论是基于代理的框架像Spring AOP或者是成熟的AOP框架,比如AspectJ都是十分有价值的,并且他们是相互补充的关系,而并非竞争的关系。Spring通过AspectJ无缝地集成了Spring AOP和IoC,使得所有基于Spring的应用架构能够方便的集成AOP。这种集成不会影响Spring AOP的API或者AOP联盟的API:Spring AOP仍然保持向后兼容。
Spring框架的核心原则之一就是non-invasiveness(轻量级),这一概念的意思就是开发者不必强制引入一些框架的类和接口到应用的业务层或者是模型层。当然,有的时候,Spring框架也支持开发者引入框架的特定依赖:之所以Spring提供这样的支持,是因为在某些场景下,使用Spring框架的特定类能更简单的实现这些功能。Spring框架(几乎)总会提供这样的选择。开发者可以自由选择是否需要使用来最佳的贴合实际的应用场景。
开发者还可以根据自己的场景来使用不同的AOP框架。开发者使用AspectJ或者Spring AOP都是没有问题的,亦或是使用@AspectJ注解的方式,或者是Spring XML配置的方式,都可以。本章更多介绍的是@AspectJ的方式,是因为Spring团队更偏好基于注解的方式。
AOP 代理
Spring AOP默认情况下是使用标准的JDK 动态代理来作为AOP的代理的,它可以使得任何接口被代理。
Spring AOP也可以使用CGLIB代理。这种代理更多的是代理类,而不是接口。CGLIB默认情况下,是当一个业务对象没有实现接口的时候,来代理的。当然,好的编程习惯肯定最好是基于接口的。业务类通常也会实现多个业务接口。当然,强制使用CGLIB作为代理也是可以的。但是在那种情况下,开发者需要声明方法为并非实现接口的方法,或者需要传递一个代理对象到方法作为具体的类型。
理解Spring AOP是基于代理实现的是非常重要的。在后续的文章中可以看到具体的例子。
更多推荐
所有评论(0)