(转)springAOP解析-1

原文:http://hzbook.group.iteye.com/group/wiki/2261-Spring

3.1  Spring AOP概述


3.1.1  AOP概念回顾
AOP是Aspect-Oriented Programming(面向方面编程)的简称,维基百科对它的解释如下所示。


维基百科对“AOP”相关概念的叙述
Aspect是一种新的模块化机制,用来描述分散在对象、类或函数中的横切关注点(crosscutting concern)。从关注点中分离出横切关注点是面向侧面的程序设计的核心概念。分离关注点使得解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑的代码中不再含有针对特定领域问题代码的调用,业务逻辑同特定领域问题的关系通过侧面来封装、维护,这样原本分散在整个应用程序中的变动就可以很好地管理起来。


这里提到的概念是从模块化出发的,面向对象设计其实也是一种模块化的方法,它把相关的数据及其处理方法放在了一起,与单纯的使用子函数进行封装相比,面向对象的模块化特性更完备,它体现了计算的一个基本原则——让计算尽可能地靠近数据。这样一来,代码组织起来就更加整齐和清晰,一个类就是一个基本的模块。很多程序的功能还可以通过设计类的继承关系而得到重用,进一步提高了开发效率。后来又出现了各种各样的设计模式供我们使用,让我们在设计程序功能时更加得心应手。


虽然我们利用面向对象的方法可以很好地组织代码,也可以通过继承关系实现代码重用,但是程序中总是会出现一些重复的代码,而且不太方便使用继承的方法把它们重用和管理起来。它们功能重复并且需要作用在不同的地方,虽然可以对这些代码做一些简单的封装,使之成为一些公共函数,但是在这种显式的调用中,使用它们并不是很方便。例如,这个公共函数在什么情况下可以使用,能不能更灵活地使用,等等。


在使用这些公共函数时,往往也需要进行一些逻辑设计。也就是需要代码实现来支持,而这些逻辑代码也是需要维护的。这时就是AOP大显身手的时候了,使用AOP后,不仅可以将这些重复的代码抽取出来单独维护,在需要使用时统一调用这些公共代码,还可以为如何使用这些公共代码提供丰富灵活的手段。这虽然与设计公共子模块有几分相似,但在传统的公共子模块调用中,除了直接硬调用之外并没有其他的手段,而AOP为处理这一类问题提供了一套完整的理论和灵活多样的实现方法。也就是说,通过AOP提出横切的概念以后,在把模块功能正交化的同时,也在此基础上提供了一系列横切的灵活实现。比如通过使用Proxy代理对象、拦截器、字节码翻译技术等,通过这一系列已有的AOP或者AOP实现技术,来实现切面应用的各种编织实现和环绕增强;为了更好地应用AOP技术,技术专家们还成立了AOP联盟来探讨AOP的标准化,有了这些支持,AOP的发展就更快了。关于AOP技术,可以到AOP联盟的文档里找到一些相关的介绍,从而提高我们对AOP的理解。比如在AOP联盟的网站上提到的以下AOP技术:


* AspectJ:源代码和字节码级别的编织器,用户需要使用不同于Java的新语言。
* AspectWerkz:AOP框架,使用字节码动态编织器和XML配置。
* JBoss-AOP:基于拦截器和元数据的AOP框架,运行在JBoss应用服务器上。

 

以及在AOP中使用到的一些相关的技术实现:


* BCEL(Byte-Code Engineering Library):Java字节码操作类库,具体的信息可以参见项目网站:http://jakarta.apache.org/bcel/
* Javassist:Java字节码操作类库,JBoss的一个子项目,项目信息可以参见项目网站:http://jboss.org/javassist/


对应于现有的AOP实现方案,AOP联盟对它们进行了一定程度的抽象,从而定义出AOP体系结构,结合这个体系结构去了解AOP技术,对我们理解AOP的概念是非常有帮助的,这个AOP体系结构如图3-1所示。



 

图3-1  AOP联盟定义的AOP体系结构


AOP联盟定义的AOP体系结构把与AOP相关的概念大致分为了由高到低、从使用到实现的三个层次。从上往下,最高层是语言和开发环境,在这个环境中可以看到几个重要的概念:基础可以视为待增强对象或者说目标对象;切面通常包含对于基础的增强应用;配置可以看成是一种编织或者说配置,通过在AOP体系中提供这个配置环境,可以把基础和切面结合起来,从而完成切面对目标对象的编织实现。


在Spring AOP实现中,是使用Java语言来实现增强对象与切面增强应用的,并为这两者的结合提供了配置环境。对于编织配置,可以使用IoC容器来完成;对于POJO对象的配置,本来就是Spring的核心IoC容器的强项。因此,对于使用Spring的AOP开发而言,使用POJO就能完成AOP任务。但是,对于其他的AOP实现方案,可能需要使用特定的实现语言、配置环境甚至是特定的编译环境。例如在AspectJ中,尽管切面增强的对象是Java对象,但却需要使用特定的Aspect语言和AspectJ编译器。AOP体系结构的第二个层次是为语言和开发环境提支持的,在这个层次中可以看到AOP框架的高层实现,主要包括配置和编织实现两部分内容。例如配置逻辑和编织逻辑实现本身,以及对这些实现进行抽象的一些高层API封装。这些实现和API封装,为前面提到的语言和开发环境的实现提供了有力的支持。


最底层是编织的具体实现模块,图3-1中看到的各种技术,都可以作为编织逻辑的具体实现方法,比如反射、程序处理、拦截器框架、类装载框架、元数据处理等。阅读完本章对Spring AOP实现原理的分析,我们会了解到,在Spring AOP中,使用的是Java本身的语言特性,比如Java Proxy代理类、拦截器这些技术,来完成AOP编织的实现。


对Spring平台或者说生态系统来说,AOP是Spring框架的核心功能模块之一。AOP与IoC容器的结合使用, 为应用开发或者Spring自身功能的扩展都提供了许多便利。

 

Spring AOP的实现和其他特性的实现一样,除了可以使用Spring本身提供的AOP实现之外,还封装了业界优秀的AOP解决方案AspectJ来让应用使用。在本章中,我们主要对Spring自身的AOP实现原理进行分析;在这个AOP实现中,Spring充分利用了IoC容器Proxy代理对象以及AOP拦截器的功能特性,通过这些对AOP基本功能的封装机制,为用户提供了AOP的实现框架。所以,要了解这些AOP的基本实现,需要我们对Java 的Proxy机制有一些基本了解。在Spring中,有一些相关的概念与AOP设计相对应;在本章的内容中,按照笔者个人的粗浅理解,我们会结合Spring的AOP实现,温故而知新,在以下的小节中对一些相关的AOP概念,先简单地回顾一下;然后再逐步展开对AOP实现原理的分析,包在这些实现原理的分析中,包括了代理对象的生成,AOP拦截器的实现,等等。这里以ProxyFactoryBean和ProxyFactory为例子来进行说明。通过分析它们背后隐藏的实现原理来了解Spring的AOP模块。和前面一样,我们需要在Eclipse环境中导入对应的AOP代码模块来辅助这些原理实现的分析,具体需要导入的包是:org.springframework.aop,这个代码包里包含了Spring AOP的实现代码。


3.1.2  Advice通知
Advice(通知):定义在连接点做什么,为切面增强提供织入接口。在Spring AOP中,它主要描述Spring AOP围绕方法调用而注入的切面行为。Advice是AOP联盟定义的一个接口,具体的接口定义在org.aopalliance.aop.Advice中。在Spring AOP的实现中,使用了这个统一接口,并通过这个接口为AOP切面增强的织入功能做了更多的细化和扩展,比如提供了更具体的通知类型,像BeforeAdvice、AfterAdvice、ThrowAdvice等。作为Spring AOP定义的接口类,具体的切面增强可以通过这些接口集成到AOP框架中去发挥作用。对于这些接口类,我们下面会进行逐一详细地讨论,我们从接口BeforeAdvice开始,首先了解它的类层次关系,如图3-2所示。



 

图3-2  BeforeAdvice的类层次关系


在BeforeAdvice的继承关系中,定义了为待增强的目标方法设置的前置增强接口MethodBeforeAdvice,使用这个前置接口需要实现一个回调函数:

Java代码  收藏代码
  1. void before(Method method, Object[] args, Object target) throws Throwable;  

 

作为回调函数,before方法的实现在advice中被配置到目标方法后,会在调用目标方法时被回调。具体的调用参数有:Method对象,这个参数是目标方法的反射对象;object[]对象数组,这个对象数组中包含的是目标方法的输入参数。以CountingBeforeAdvice为例,用它来说明BeforeAdvice的具体使用,CountingBeforeAdvice是接口MethodBeforeAdvice的具体实现,如代码清单3-1所示,它的实现比较简单,完成的工作是统计被调用的方法次数。作为切面增强实现,它会根据调用方法的方法名进行统计,把统计结果根据方法名和调用次数作为键值对放入一个map中。


代码清单3-1  CountingBeforeAdvice的实现

Java代码  收藏代码
  1. public class CountingBeforeAdvice extends MethodCounter implements MethodBeforeAdvice {  
  2. //实现before回调接口,这是接口MethodBeforeAdvice的要求。  
  3. public void before(Method m, Object[] args, Object target) throws Throwable {  
  4.  count(m);  
  5. }  
  6. }  

 

这里调用了count方法,使用了目标方法的反射对象作为参数,完成对调用方法名的统计工作。count方法在CountingBeforeAdvice的基类MethodCounter中实现,如代码清单3-2所示。这个切面增强完成的统计实现并不复杂,它在对象中维护一个哈希表,用来存储统计数据。在统计过程中,首先通过目标方法的反射对象得到方法名,然后进行累加,把统计结果放到维护的哈希表中。如果需要统计数据,就到这个哈希表中根据key来获取。


代码清单3-2  MethodCounter实现统计目标方法调用次数

Java代码  收藏代码
  1. public class MethodCounter implements Serializable {  
  2. // 这个Hashmap用来存储方法名和调用次数的键值对。  
  3. private HashMap<String, Integer> map = new HashMap<String, Integer>();  
  4. //所有的调用次数,不管是什么方法名。  
  5. private int allCount;  
  6. //CountingBeforeAdvice的调用入口。  
  7. protected void count(Method m) {  
  8.  count(m.getName());  
  9. }  
  10. //根据目标方法的方法名统计调用次数。  
  11. protected void count(String methodName) {  
  12.  Integer i = map.get(methodName);  
  13.  i = (i != null) ? new Integer(i.intValue() + 1) : new Integer(1);  
  14.  map.put(methodName, i);  
  15.  ++allCount;  
  16. }  
  17. //根据方法名取得调用的次数。  
  18. public int getCalls(String methodName) {  
  19.  Integer i = map.get(methodName);  
  20.  return (i != null ? i.intValue() : 0);  
  21. }  
  22. //取得所有的方法调用次数。  
  23. public int getCalls() {  
  24.  return allCount;  
  25. }  
  26. public boolean equals(Object other) {  
  27.  return (other != null && other.getClass() == this.getClass());  
  28. }  
  29.   
  30. public int hashCode() {  
  31.  return getClass().hashCode();  
  32. }  
  33. }  

 

在Advice的实现体系中,Spring还提供了AfterAdvice的这种通知类型,它的类接口关系如图3-3所示。



 

图3-3  AfterAdvice的接口关系


在如图3-3所示的AfterAdvice类接口关系中,有一系列对AfterAdvice的实现和接口扩展,比如AfterReturnningAdvice。我们以AfterReturningAdvice通知的实现为例,分析一下AfterAdvice通知类型的实现原理。在AfterReturningAdvice接口中定义了接口方法,如下所示。

 

Java代码  收藏代码
  1. void afterReturning(Object returnValue, Method method, Object[] args, Object  
  2. target) throws Throwable;  

 

AfterReturing方法也是一个回调函数,AOP应用需要在这个接口实现中,提供切面增强的具体设计,在这个Advice通知被正确配置以后,在目标方法调用结束并成功返回的时候,接口实现会被Spring AOP回调。对于回调参数,有目标方法的返回结果、反射对象以及调用参数(AOP把这些参数都封装在一个对象数组中传递进来)等。与前面分析BeforeAdvice一样,在Spring AOP的包中,同样可以看到一个CountingAfterReturningAdvice,作为熟悉AfterRetur-
ningAdvice使用的例子;它的实现基本上与CountingBeforeAdvice是一样的,如代码清单3-3所示。


代码清单3-3  CountingAfterReturningAdvice的实现

Java代码  收藏代码
  1. public class CountingAfterReturningAdvice extends MethodCounter implements  
  2. AfterReturningAdvice {  
  3. public void afterReturning(Object o, Method m, Object[] args, Object target)  
  4. throws Throwable {  
  5.  count(m);  
  6. }  
  7. }  

 

在实现AfterReturningAdvice的接口方法AfterReturning中,可以调用MethodCounter的count方法,从而完成根据方法名来对目标方法调用的次数进行统计。

 

count方法调用的实现与CountingBeforeAdvice基本上是一样的,所不同的是调用发生的时间。尽管增强逻辑相同,但是,如果它实现不同的AOP通知接口,就会被AOP编织到不同的调用场合中。尽管它们完成的增强行为是一样的,都是根据目标方法名对调用次数进行统计,但是它们的最终实现却有很大不同,一个是在目标方法调用前实现切面增强,一个是在目标方法成功调用返回结果后实现切面增强。由此可见,AOP技术给应用带来的灵活性,使得相同的代码完全可以根据应用的需要灵活地出现在不同的应用场合。


了解了BeforeAdvice和AfterAdvice,在Spring AOP中还可以看到另外一种Advice通知类型——ThrowsAdvice,它的类层次关系如图3-4所示。



 

图3-4  ThrowsAdvice的类层次关系


ThrowsAdvice并没有指定需要实现的接口方法,它在抛出异常时被回调,这个回调是AOP使用反射机制来完成的。我们可以通过CountingThrowsAdvice了解ThrowsAdvice的使用方法,如代码清单3-4所示。


代码清单3-4  CountingThrowsAdvice的实现

Java代码  收藏代码
  1. public static class CountingThrowsAdvice extends MethodCounter implements ThrowsAdvice {  
  2.  public void afterThrowing(IOException ex) throws Throwable {  
  3.   count(IOException.class.getName());  
  4.  }  
  5.   
  6.  public void afterThrowing(UncheckedException ex) throws Throwable {  
  7.   count(UncheckedException.class.getName());  
  8.  }  
  9. }  

 

在AfterThrowing方法中,从输入的异常对象中得到异常的名字并进行统计。count方法同样是在MethodCounter中实现,与前面的两个Advice的实现相同,只是CountingBeforeAdvice和CountingAfterReturningAdvice统计的是目标方法的调用次数。在这里,count方法完成的是根据异常名称统计抛出异常的次数。


3.1.3  Pointcut切点
Pointcut(切点):决定Advice通知应该作用于哪个连接点,也就是说通过Pointcut切点来定义需要增强的方法的集合,这些集合的选取可以按照一定的规则来完成。在这种情况下,Pointcut通常意味着标识方法,例如,这些需要增强的地方可以是被某个正则表达式进行标识,或根据某个方法名进行匹配等。


为了方便用户使用,Spring AOP提供了具体的切点供用户使用,关于切点,可以看看Spring AOP中的类继承体系,如图3-5所示。



 

图3-5  Spring AOP的Pointcut类继承关系


在Pointcut的基本接口定义中可以看到,需要返回一个MethodMatcher;对于Point的匹配判断功能,具体是由这个返回的MethodMatcher来完成的,也就是说,由这个MethodMatcher来判断是否需要对当前方法调用进行增强,或者说对当前调用方法是否需要应用配置好的advice通知。在Pointcut的类继承关系中,我们以正则表达式切点 JdkRegexpMethodPointcut的实现原理为例,来具体了解切点Pointcut的工作原理。JdkRegexpMethodPointcut类完成通过正则表达式对方法名进行匹配的功能。在JdkRegexpMethodPointcut的基类 StaticMethodMatcherPointcut的实现中可以看到,设置的MethodMatcher为StaticMethodMatcher,同时JdkRegexpMethodPointcut也是这个MethodMatcher的子类,它的类层次关系如图3-6所示。



 

图3-6  StaticMethodMatcher的类层次关系


如图3-6所示,在Pointcut中,通过这样的类继承关系,MethodMatcher对象实际上是可以被配置成JdkRegexpMethodPointcut来完成方法的匹配判断的。在JdkRegexpMethod-
Pointcut中,可以看到matches方法,这个方法是MethodMatcher定义的接口方法;在JdkRegexpMethodPointcut的实现中,这个matches方法就是使用正则表达式来对方法名进行匹配的地方。关于在AOP框架中对matches方法的调用,我们会在下面具体分析Spring AOP实现中看到。要了解matches在AOP框架中的调用位置,比较简单的方法就是从matches方法作为起始点,去对它的方法调用关系进行追溯,可以看到对matches方法的调用关系如图3-7所示。



 

图3-7  对JdkRegexpMethodPointcut的matches方法的调用关系


在对matches方法的调用关系中可以看到,是JdkDynamicAopProxy的invoke方法中触发了对matches方法的调用,熟悉Proxy使用的读者,一定会想到,这个invoke方法应该就是Proxy对象进行代理回调的入口方法,这个invoke回调的实现是使用JDK动态代理完成AOP功能的一部分,关于这部分实现原理,在下面AOP的实现分析中有详细的阐述。我们这里的重点是了解Pointcut的实现原理,比如去具体看看matches本身的实现,对于JdkRegexpMethod-
Pointcut的matches方法,它的是实现如代码清单3-5所示。


代码清单3-5  JdkRegexpMethodPointcut使用matches完成匹配

Java代码  收藏代码
  1. protected boolean matches(String pattern, int patternIndex) {  
  2.  Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern);  
  3.  return matcher.matches();  
  4. }  

 

在JDKRegexpMethodPointcut中,通过JDK来实现正则表达式的匹配,在代码清单3-5中可以看得很清楚;如果要详细了解使用JDK的正则表达式匹配功能,可以参考JDK的API文档。我们接着看看其他的Pointcut。


在Spring AOP中,还提供了其他的MethodPointcut,比如通过方法名匹配进行Advice匹配的NameMatchMethodPointcut。它的matches方法实现很简单,匹配的条件是方法名相同或者方法名相匹配,如代码清单3-6所示。


代码清单3-6  NameMatchMethodPointcut的matches

Java代码  收藏代码
  1. public boolean matches(Method method, Class targetClass) {  
  2.  for (String mappedName : this.mappedNames) {  
  3.   if (mappedName.equals(method.getName()) || isMatch(method.getName(),  
  4.  mappedName)) {  
  5.    return true;  
  6.   }  
  7.  }  
  8.  return false;  
  9. }  
  10. protected boolean isMatch(String methodName, String mappedName) {  
  11.  return PatternMatchUtils.simpleMatch(mappedName, methodName);  
  12. }  

 
3.1.4  Advisor通知器
Advisor(通知器):当我们完成对目标方法的切面增强设计(advice)和关注点的设计(pointcut)以后,需要一个对象把它们结合起来,完成这个作用的就是Advisor。通过Advisor,可以定义应该使用哪个Advice并在哪个Pointcut使用它,也就是说通过Advisor把Advice和Pointcut结合起来了,这个结合为应用使用IoC容器配置AOP应用,或者说即开即用地使用AOP基础设施。在Spring AOP中,我们举一个Advisor的实现(DefaultPointcutAdvisor)作为例子,去了解Advisor的工作原理。在DefaultPointcutAdvisor中有两个属性,分别是Advice和Pointcut,通过这两个属性,可以分别配置Advice和Pointcut,DefaultPointcutAdvisor的实现如代码清单3-7所示。


代码清单3-7  DefaultPointcutAdvisor的实现

Java代码  收藏代码
  1. public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor  
  2. implements Serializable {  
  3.   
  4. private Pointcut pointcut = Pointcut.TRUE;  
  5. public DefaultPointcutAdvisor() {  
  6. }  
  7. public DefaultPointcutAdvisor(Advice advice) {  
  8.  this(Pointcut.TRUE, advice);  
  9. }  
  10. public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {  
  11.  this.pointcut = pointcut;  
  12.  setAdvice(advice);  
  13. }  
  14. public void setPointcut(Pointcut pointcut) {  
  15.  this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);  
  16. }  
  17. public Pointcut getPointcut() {  
  18.  return this.pointcut;  
  19. }  
  20. public String toString() {  
  21.  return getClass().getName() + ": pointcut [" + getPointcut() + "]; advice ["  
  22. + getAdvice() + "]";  
  23. }  
  24. }  

 

在DefaultPointcutAdvisor中默认的Pointcut被设置为Pointcut.True,这个Pointcut.True是在Pointcut接口中被定为的:

Java代码  收藏代码
  1. Pointcut TRUE = TruePointcut.INSTANCE;  

 

TruePointcut的INSTANCE是一个单件。在它的实现中,可以看到单件模式的具体应用和它的典型使用方法,比如使用static类变量来持有单件实例,使用private私有构造函数来确保除了在当前单件实现中,单件不会再次被创建和实例化,从而保证它的“单件”特性。在TruePointcut的methodMatcher实现中,使用的是TrueMethodMatcher作为方法匹配器。在这个方法匹配器中,它对任何的方法匹配要求都返回true的结果,也就是说对任何方法名的匹配要求,它都会返回匹配成功的结果,和TruePointcut一样,这个TrueMethodMatcher也是一个单件实现。TruePointcut和TrueMethodMatcher的实现如代码清单3-8和代码清单3-9所示。


代码清单3-8  TruePointcut的实现

Java代码  收藏代码
  1. class TruePointcut implements Pointcut, Serializable {  
  2. public static final TruePointcut INSTANCE = new TruePointcut();  
  3. // Enforce Singleton pattern.  
  4. private TruePointcut() {  
  5. }  
  6. public ClassFilter getClassFilter() {  
  7.  return ClassFilter.TRUE;  
  8. }  
  9. public MethodMatcher getMethodMatcher() {  
  10.  return MethodMatcher.TRUE;  
  11. }  

 

代码清单3-9  TrueMethodMatcher的实现

Java代码  收藏代码
  1. class TrueMethodMatcher implements MethodMatcher, Serializable {  
  2. public static final TrueMethodMatcher INSTANCE = new TrueMethodMatcher();  
  3. // Enforce Singleton pattern.  
  4.   
  5. private TrueMethodMatcher() {  
  6. }  
  7. public boolean isRuntime() {  
  8.  return false;  
  9. }  
  10. public boolean matches(Method method, Class targetClass) {  
  11.  return true;  
  12. }  
  13. public boolean matches(Method method, Class targetClass, Object[] args) {  
  14.  // Should never be invoked as isRuntime returns false.  
  15.  throw new UnsupportedOperationException();  
  16. }  
  17. private Object readResolve() {  
  18.  return INSTANCE;  
  19. }  
  20.   
  21. public String toString() {  
  22.  return "MethodMatcher.TRUE";  
  23. }  
  24. }  

 


3.2  建立AopProxy代理对象


3.2.1  配置ProxyFactoryBean
从这部分开始,我们进入到Spring AOP的实现部分,在分析Spring AOP的实现原理中,我们主要以ProxyFactoryBean的实现作为例子和实现的基本线索进行分析。很大一个原因是因为ProxyFactoryBean是在Spring IoC环境中,创建AOP应用的最底层方法,也是最灵活的方法,Spring通过它完成了对AOP使用的封装,以它的实现为入口逐层深入,是很好的一条帮助我们理解Spring AOP实现的学习路径。


在了解ProxyFactoryBean的实现之前,我们先简要地了解一下ProxyFactoryBean的配置和使用,在基于XML配置Spring的Bean的时候,往往需要一系列的配置步骤来使用ProxyFactoryBean和AOP,比如以下这些步骤:


1)定义使用的通知器Advisor,这个通知器应该作为一个Bean来定义。很重要的一点,这个通知器的实现定义了需要对目标对象进行增强的切面行为,也就是Advice通知。
2)定义proxyFactoryBean,把它作为另一个Bean来定义,它是封装AOP功能的主要类。在配置ProxyFactoryBean时,需要设定与AOP实现相关的重要属性,比如ProxyInterface、interceptorNames和target等。从属性名称可以看出,interceptorNames属性的值往往设置为需要定义的通知器,因为这些通知器在ProxyFactoryBean的AOP配置下,是通过使用代理对象的拦截器机制起作用的。所以,这里依然沿用了拦截器这个名字,也算是旧瓶装新酒吧。
3)定义target属性,作为target属性注入的Bean,是需要用AOP通知器中的切面应用来增强的对象,也就是前面我们提到的base对象。


有了这些配置,就可以使用ProxyFactoryBean完成AOP的基本功能了。关于配置的例子,如代码清单3-10所示。与前面提到的配置步骤相对应,除了定义了ProxyFactoryBean的AOP封装,还定义了一个Advisor,取名为testAdvisor。作为ProxyFactory配置的一部分,还需要配置拦截的方法调用接口和目标对象。这些基本的配置,是使用ProxyFactoryBean实现AOP功能的重要组成,对于这些配置的实现和作用机制,也是我们后面重点分析的内容。


代码清单3-10  配置ProxyFactoryBean

Java代码  收藏代码
  1. <bean id="testAdvisor" class="com.abc.TestAdvisor"/>  
  2. <bean id="testAOP" class="org.springframework.aop.ProxyFactoryBean>   
  3.  <property name="proxyInterfaces"><value>com.test.AbcInterface"</value></property>  
  4.  <property name="target">  
  5.  <bean class="com.abc.TestTarget"/>  
  6.   </property>  
  7.   <property name="interceptorNames">  
  8.  <list><value> testAdvisor</value></list>  
  9.   </property>  
  10. </bean>  

 

掌握这些配置后,就可以具体看一看这些AOP是如何实现的。也就是说,切面应用是怎样通过ProxyFactoryBean对target对象起作用的。对于这个ProxyFactoryBean,我们先了解一下ProxyFactoryBean的类层次关系,如图3-8所示。在这个类层次关系中,可以看到用来完成AOP应用的类,比如AspectJProxyFactory、ProxyFactory和ProxyFactoryBean,它们都在同一个类的继承体系下,都是ProxyConfig、AdvisedSupport和ProxyCreatorSupport的子类。作为大家的共同基类,ProxyConfig可以看成是一个数据类,这个数据基类为像ProxyFactoryBean这样的子类提供了配置属性。在另一个基类AdvisedSupport的实现中,它封装了AOP中对通知和通知器的相关操作,这些操作对于不同的AOP的代理对象的生成都是一样的,但对于具体的AOP代理对象的创建,AdvisedSupport把它交给它的子类们去完成。ProxyCreatorSupport看成是其子类创建AOP代理对象的一个辅助类,通过继承以上提到的基类的功能实现,具体的AOP代理对象的生成,根据不同的需要分别由ProxyFactoryBean、AspectJProxyFactory和ProxyFactory来完成。对于需要使用AspectJ的AOP应用,AspectJProxyFactory起到集成Spring和AspectJ的作用;对于使用Spring AOP的应用,ProxyFactoryBean和ProxyFactoy都提供了AOP功能的封装,只是如果使用ProxyFactoryBean,可以在IoC容器中完成声明式配置,而使用ProxyFactory,则需要编程式的使用Spring AOP的功能;对于它们是如何封装实现AOP功能的,在本章后面的小节中给出详细的分析,在这里,通过这些类层次关系的了解,希望能先给读者留下一个大致的印象。



 

图3-8  ProxyFactoryBean相关的类层次关系


3.2.2  ProxyFactoryBean生成AopProxy
在Spring AOP的使用中,我们已经了解到,可以通过ProxyFactoryBean来配置目标对象和方面行为。这个ProxyFactoryBean是一个FactoryBean,对FactoryBean这种Spring应用中经常出现的Bean的工作形式,读者一定不会感到陌生,对于FactoryBean的工作原理,我们在结合IoC容器的实现原理分析中已经做过阐述。在ProxyFactoryBean中,通过interceptorNames属性来配置已经定义好的通知器Advisor。虽然这里的名字叫interceptNames,但值得注意的是,实际上却是供AOP应用配置通知器的地方。在ProxyFactoryBean中,需要为target目标对象生成Proxy代理对象,从而为AOP横切面的编织做好准备工作。这些具体的代理对象生成工作,在以后的实现原理分析中,我们可以看到是通过JDK的Proxy或CGLIB来完成的。


在ProxyFactoryBean中,它的AOP实现需要依赖JDK或者CGLIB提供的Proxy特性。从FactoryBean中获取对象,是用getObject()方法作为入口完成的;让我们进入ProxyFactoryBean的实现中去,这个我们一点也不陌生的getObject方法,是FactoryBean需要实现的接口。对ProxyFactoryBean来说,把需要对target目标对象增加的增强处理,都通过getObject方法进行封装了,这些增强处理是为AOP功能的实现提供服务的。对于getObject的实现如代码清单3-11所示。getObject方法首先对通知器链进行初始化,通知器链封装了一系列的拦截器,这些拦截器都要从配置中读取,然后为代理对象的生成做好准备。在生成代理对象的时候,因为Spring中有sigleton类型和prototype类型这两种不同的Bean,所以这里对代理对象的生成需要做一个区分。


代码清单3-11  ProxyFactoryBean的getObject

Java代码  收藏代码
  1. public Object getObject() throws BeansException {  
  2.  //这里初始化通知器链。  
  3.  initializeAdvisorChain();  
  4.  //这里对singleton和protype的类型进行区分,生成对应的proxy。  
  5.  if (isSingleton()) {  
  6.   return getSingletonInstance();  
  7.  }  
  8.  else {  
  9.   if (this.targetName == null) {  
  10.    logger.warn("Using non-singleton proxies with singleton targets is  
  11. often undesirable. " +  
  12.      "Enable prototype proxies by setting the 'targetName' property.");  
  13.   }  
  14.   return newPrototypeInstance();  
  15.  }  
  16. }  

 

 为Proxy代理对象配置Advisor链是在initializeAdvisorChain方法中完成的,如代码清单3-12所示。这个初始化过程有一个标志位advisorChainInitialized,这个标志用来表示通知器链是否已经初始化。如果已经初始化,那么这里就不会再初始化,而是直接返回。也就是说,这个初始化的工作,发生在应用第一次通过ProxyFactoryBean去获取代理对象的时候。在完成这个初始化之后,接着会读取配置中出现的所有通知器,这个取得通知器的过程也比较简单,把通知器的名字交给容器的getBean方法就可以了,这是通过对IoC容器实现的一个回调来完成的。然后把从IoC容器中取得的通知器加入到拦截器链中,这个动作是由addAdvisorOn-
ChainCreation方法来实现的。


代码清单3-12  对Advisor配置链的初始化

Java代码  收藏代码
  1. private synchronized void initializeAdvisorChain() throws AopConfigException,  
  2. BeansException {  
  3.  if (this.advisorChainInitialized) {  
  4.   return;  
  5.  }  
  6.  if (!ObjectUtils.isEmpty(this.interceptorNames)) {  
  7.   if (this.beanFactory == null) {  
  8.    throw new IllegalStateException("No BeanFactory available anymore  
  9.    (probably due to serialization) " +  
  10.    "- cannot resolve interceptor names " + Arrays.asList(this.  
  11.   interceptorNames));  
  12.   }  
  13.   /** 
  14.   * Globals can't be last unless we specified a targetSource 
  15.   * using the property... 
  16.   */  
  17.   if (this.interceptorNames[this.interceptorNames.length - 1].endsWith  
  18.  (GLOBAL_SUFFIX) && this.targetName == null && this.targetSource ==  
  19.  EMPTY_TARGET_  
  20. SOURCE) {  
  21.    throw new AopConfigException("Target required after globals");  
  22.   }  
  23.   
  24.   // Materialize interceptor chain from bean names.  
  25.   // 这里是添加advisor链的调用,是通过interceptorNames属性来进行配置的。  
  26.   for (String name : this.interceptorNames) {  
  27.    if (logger.isTraceEnabled()) {  
  28.     logger.trace("Configuring advisor or advice '" + name + "'");  
  29.    }  
  30.   
  31.    if (name.endsWith(GLOBAL_SUFFIX)) {  
  32.     if (!(this.beanFactory instanceof ListableBeanFactory)) {  
  33.      throw new AopConfigException(  
  34.        "Can only use global advisors or interceptors with  
  35.       a ListableBeanFactory");  
  36.     }  
  37.     addGlobalAdvisor((ListableBeanFactory) this.beanFactory,  
  38.       name.substring(0, name.length() - GLOBAL_SUFFIX.length()));  
  39.    }  
  40.   
  41.    else {  
  42.     // If we get here, we need to add a named interceptor.  
  43.     // We must check if it's a singleton or prototype.  
  44.     Object advice;  
  45.     if (this.singleton || this.beanFactory.isSingleton(name)) {  
  46.      // Add the real Advisor/Advice to the chain.  
  47.      advice = this.beanFactory.getBean(name);  
  48.     }  
  49.     else {  
  50.      // It's a prototype Advice or Advisor: replace with a prototype.  
  51.      /** 
  52.      *Avoid unnecessary creation of prototype bean just for 
  53.      *advisor chain initialization. 
  54.      */  
  55.      advice = new PrototypePlaceholderAdvisor(name);  
  56.     }  
  57.     addAdvisorOnChainCreation(advice, name);  
  58.    }  
  59.   }  
  60.  }  
  61.  this.advisorChainInitialized = true;  
  62. }  

 

生成singleton的代理对象在getSingletonInstance()的代码中完成,这个方法是ProxyFactoryBean生成AopProxy代理对象的调用入口。在代理对象中会封装对target目标对象的调用,也就是说针对target对象的方法调用行为,会被这里生成的代理对象所拦截。具体的生成过程是,首先需要读取ProxyFactoryBean中的配置,为生成代理对象做好必要的准备,比如设置代理的方法调用接口等。对于在getSingletonInstance()方法中代理对象的生成过程,如代码清单3-13所示。


代码清单3-13  生成单件代理对象

Java代码  收藏代码
  1. private synchronized Object getSingletonInstance() {  
  2.  if (this.singletonInstance == null) {  
  3.   this.targetSource = freshTargetSource();  
  4.   if (this.autodetectInterfaces && getProxiedInterfaces().length == 0  
  5.  && !isProxyTargetClass()) {  
  6.    // Rely on AOP infrastructure to tell us what interfaces to proxy.  
  7.    Class targetClass = getTargetClass();  
  8.    if (targetClass == null) {  
  9.     throw new FactoryBeanNotInitializedException("Cannot determine  
  10.    target class for proxy");  
  11.    }  
  12. // 这里设置代理对象的接口。  
  13. setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));  
  14.   }  
  15.   // Initialize the shared singleton instance.  
  16.   super.setFrozen(this.freezeProxy);  
  17.   // 注意这里的方法会使用ProxyFactory来生成我们需要的Proxy。  
  18.   this.singletonInstance = getProxy(createAopProxy());  
  19.  }  
  20.  return this.singletonInstance;  
  21. }  
  22. //使用createAopProxy返回的AopProxy来得到代理对象。  
  23. protected Object getProxy(AopProxy aopProxy) {  
  24.  return aopProxy.getProxy(this.proxyClassLoader);  
  25. }  

 

这里出现了AopProxy类型的对象,Spring使用这个AopProxy接口类把AOP代理对象的实现与框架的其他部分有效地分离开来。AopProxy是一个接口,它有两个子类实现,一个是Cglib2AopProxy,另一个是JdkDynamicProxy。顾名思义,对这两个AopProxy接口的子类实现,Spring分别使用CGLIB和JDK来生成需要的Proxy代理对象。


具体的代理对象的生成,是在ProxyFactoryBean的基类AdvisedSupport的实现中通过AopProxyFactory完成的,这个代理对象要么从JDK中生成,要么借助CGLIB获得。因为ProxyFactoryBean本身就是AdvisedSupport的子类,在ProxyFactoryBean中获得AopProxy是很方便的,具体的AopProxy生成过程,我们可以在ProxyCreatorSupport中看到。至于需要生成什么样的代理对象,信息都封装在AdvisedSupport里,这个对象也是生成AopProxy的方法的输入参数,这里设置为this本身,因为ProxyCreatorSupport本身就是AdvisedSupport的子类。在ProxyCreatorSupport中生成代理对象的入口实现,如代码清单3-14所示。


代码清单3-14  ProxyCreatorSupport生成AopProxy对象

Java代码  收藏代码
  1. protected final synchronized AopProxy createAopProxy() {  
  2.  if (!this.active) {  
  3.   activate();  
  4.  }  
  5.  /** 
  6.  *通过AopProxyFactory取得AopProxy,这个AopProxyFactory是在 
  7.  *初始化函数中定义的,使用的是DefaultAopProxyFactory。 
  8.  */  
  9.  return getAopProxyFactory().createAopProxy(this);  
  10. }  

 

这里使用了AopProxyFactory来创建AopProxy,AopProxyFactory使用的是DefaultAopProxyFactory。这个被使用的AopProxyFactory,作为AopProxy的创建工厂对象,是在ProxyFactoryBean的基类ProxyCreatorSupport中被创建的。在创建AopProxyFactory的时候,它被设置为DefaultAopProxyFactory。很显然,Spring给出了这个默认的AopProxyFactory工厂的实现。有了这个AopProxyFactory对象以后,问题就转换为:在DefaultAopProxyFactory中AopProxy是怎样生成的问题了。


关于AopProxy代理对象的生成,需要考虑使用哪种生成方式,如果目标对象是接口类,那么适合使用JDK来生成代理对象,否则Spring会使用CGLIB来生成目标对象的代理对象。为了满足不同的代理对象生成的要求,DefaultAopProxyFactory作为AopProxy对象的生产工厂,可以根据不同的需要生成这两种AopProxy对象。对于AopProxy对象的生产过程,在DefaultAopProxyFactory中创建AopProxy的代码中可以清楚地看到,但这是一个比较高层次的AopProxy代理对象的生成过程,如代码清单3-15所示。所谓高层次,是指在DefaultAopProxyFactory创建AopProxy的过程中,对不同的AopProxy代理对象的生成,所涉及的生成策略和场景做了相应的设计,但是对于具体的AopProxy代理对象的生成,最终并没有由这个DefaultAopProxyFactory来完成,比如对JDK和CGLIB这些具体的技术的使用,对具体的实现层次的代理对象的生成,是由Spring封装的JdkDynamicAopProxy和CglibProxyFactory类来完成的。


在AopProxy代理对象的生成过程中,首先要从AdvisedSupport对象中取得配置的目标对象,这个目标对象是实现AOP功能所必需的,道理很简单,AOP完成的是切面应用对目标对象的增强,皮之不存,毛将焉附,这个目标对象可以看做是“皮”,而AOP切面增强就是依附于这块皮上的“毛发”。如果这里发现没有配置目标对象的话,会直接抛出异常,提醒AOP应用,需要提供正确的目标对象的配置。在对目标对象配置的检查完成以后,需要根据配置的情况来决定使用什么方式来创建AopProxy代理对象。一般而言,默认的方式是使用JDK来产生AopProxy代理对象,但是如果遇到配置的目标对象不是接口类的实现的时候,会使用CGLIB来产生AopProxy代理对象;在使用CGLIB来产生AopProxy代理对象的时候,因为CGLIB是一个第三方的类库,本身不在JDK的基本类库中,所以需要在CLASSPATH路径中正确地配置,以便能够加载和使用。在Spring中,使用JDK和CGLIB来生成AopProxy代理对象的工作,是由JdkDynamicAopProxy和CglibProxyFactory来完成的。对于详细的代理对象的生成过程,在下面的小节中,将逐个进行详细的分析。


代码清单3-15  在DefaultAopProxyFactory中创建AopProxy

Java代码  收藏代码
  1. public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {  
  2.  if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSupplied  
  3. ProxyInterfaces(config)) {  
  4.   Class targetClass = config.getTargetClass();  
  5.   if (targetClass == null) {  
  6.    throw new AopConfigException("TargetSource cannot determine target  
  7.     class: " +  
  8.    "Either an interface or a target is required for proxy creation.");  
  9.   }//如果targetClass是接口类,使用JDK来生成Proxy。  
  10.   if (targetClass.isInterface()) {  
  11.    return new JdkDynamicAopProxy(config);  
  12.   }  
  13.   if (!cglibAvailable) {  
  14.    throw new AopConfigException(  
  15.      "Cannot proxy target class because CGLIB2 is not available. " +  
  16.      "Add CGLIB to the class path or specify proxy interfaces.");  
  17.   } //如果不是接口类要生成proxy,那么使用cglib来生成。  
  18.   return CglibProxyFactory.createCglibProxy(config);  
  19.  }  
  20.  else {  
  21.   return new JdkDynamicAopProxy(config);  
  22.  }  
  23. }  

 

3.2.3  JDK生成AopProxy代理对象
我们都知道对于AopProxy代理对象,可以由JDK或CGLIB来生成,而JdkDynamicAopProxy和Cglib2AopProxy实现的都是AopProxy接口,它们的层次关系如图3-9所示。


 

 

图3-9  AopProxy接口


我们先看看在AopProxy接口实现中,JdkDynamicAopProxy是怎样完成AopProxy代理对象生成工作的。这个代理对象的生成过程如代码清单3-16所示。在JdkDynamicAopProxy中,使用了JDK的Proxy类来生成代理对象,在生成Proxy对象之前,首先需要从advised对象中取得代理对象的代理接口配置,然后调用Proxy的newProxyInstance方法,最终得到对应的Proxy代理对象;在生成代理对象的时候,需要指明三个参数,分别是类装载器、代理接口和Proxy回调方法所在的对象,这个对象需要实现InvocationHandler接口;InvocationHandler接口定义了invoke方法,提供代理对象的回调入口;对于JdkDynamicAopProxy,它本身实现了InvocationHandler接口和invoke方法,这个invoke方法是Proxy代理对象的回调方法,所以可以使用this来把JdkDynamicAopProxy指派给Proxy对象;也就是说JdkDynamicAopProxy对象本身,在Proxy代理的接口方法被调用时,会促发invoke方法的回调。在这个回调方法里,完成了AOP编织实现的封装。在这里,我们先重点关注AopProxy代理对象的生成,Proxy代理对象的invoke实现,将是我们后面详细分析AOP实现原理的重要部分。


代码清单3-16  JdkDynamicAopProxy生成Proxy代理对象

Java代码  收藏代码
  1. public Object getProxy(ClassLoader classLoader) {  
  2.  if (logger.isDebugEnabled()) {  
  3.   logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.  
  4.       getTargetSource());  
  5.  }  
  6.  Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);  
  7.  findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);  
  8.  //这里调用JDK生成Proxy的地方。  
  9.  return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);  
  10. }  

 

3.2.4  CGLIB生成AopProxy代理对象
在AopProxy接口实现中,可以看到使用CGLIB来生成Proxy代理对象,这个Proxy代理对象的生成,可以在Cglib2AopProxy的代码实现中看到,同样是在AopProxy的接口方法getProxy的实现完成的,如代码清单3-17所示。可以看到具体对CGLIB的使用,比如对Enhancer对象的配置,以及通过Enhancer对象生成代理对象的过程。在这个生成代理对象的过程中,需要注意的是对Enhancer对象callback回调的设置,正是通过这些callback回调,封装了Spring AOP的实现,就像我们在前面看到的JDK的Proxy对象的invoke回调方法一样;在Enhancer的callback回调设置中,实际上是通过设置DynamicAdvisedInterceptor拦截器来完成AOP功能的,如果读者感兴趣的话,可以在getCallbacks方法实现中看到回调DynamicAdvisedInterceptor的设置:

Java代码  收藏代码
  1. Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);  

 

对于这个DynamicAdvisedInterceptor中的回调实现,我们将在后面详细地进行分析。


代码清单3-17  Cglib2AopProxy生成AopProxy代理对象

Java代码  收藏代码
  1. public Object getProxy(ClassLoader classLoader) {  
  2.  if (logger.isDebugEnabled()) {  
  3.   logger.debug("Creating CGLIB2 proxy: target source is " + this.advised.  
  4.  getTargetSource());  
  5.  }  
  6.  //从advised中取得在IoC容器中配置的target对象。  
  7.  try {  
  8.   Class rootClass = this.advised.getTargetClass();  
  9.   Assert.state(rootClass != null, "Target class must be available for  
  10.  creating a CGLIB proxy");  
  11.   Class proxySuperClass = rootClass;  
  12.   if (AopUtils.isCglibProxyClass(rootClass)) {  
  13.    proxySuperClass = rootClass.getSuperclass();  
  14.    Class[] additionalInterfaces = rootClass.getInterfaces();  
  15.    for (Class additionalInterface : additionalInterfaces) {  
  16.     this.advised.addInterface(additionalInterface);  
  17.    }  
  18.   }  
  19.   // Validate the class, writing log messages as necessary.  
  20.   validateClassIfNecessary(proxySuperClass);  
  21.   // Configure CGLIB Enhancer...  
  22.   // 创建并配置CGLIB的Enhancer,这个Enhancer对象是CGLIB的主要操作类。  
  23.   Enhancer enhancer = createEnhancer();  
  24.   if (classLoader != null) {  
  25.    enhancer.setClassLoader(classLoader);  
  26.    if (classLoader instanceof SmartClassLoader &&  
  27.      ((SmartClassLoader) classLoader).isClassReloadable  
  28.        (proxySuperClass)) {  
  29.     enhancer.setUseCache(false);  
  30.    }  
  31.   }  
  32.   /** 
  33.   *设置Enhancer对象,包括设置代理接口,回调方法,回调方法来自 
  34.   *advised的IoC配置,比如使用AOP的DynamicAdvisedInterceptor拦截器。 
  35.   */  
  36.   enhancer.setSuperclass(proxySuperClass);  
  37.   enhancer.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowable  
  38.  Exception.class));  
  39.   enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));  
  40.   enhancer.setInterceptDuringConstruction(false);  
  41.   Callback[] callbacks = getCallbacks(rootClass);  
  42.   enhancer.setCallbacks(callbacks);  
  43.   enhancer.setCallbackFilter(new ProxyCallbackFilter(  
  44.     this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap,  
  45.    this.fixedInterceptorOffset));  
  46.   Class[] types = new Class[callbacks.length];  
  47.   for (int x = 0; x < types.length; x++) {  
  48.    types[x] = callbacks[x].getClass();  
  49.   }  
  50.   enhancer.setCallbackTypes(types);  
  51.   // 通过Enhancer生成代理对象。  
  52.   Object proxy;  
  53.   if (this.constructorArgs != null) {  
  54.    Proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);  
  55.   }  
  56.   else {  
  57.    proxy = enhancer.create();  
  58.   }  
  59.   return proxy;  
  60.  }  
  61.  catch (CodeGenerationException ex) {  
  62.   throw new AopConfigException("Could not generate CGLIB subclass of class [" +  
  63.     this.advised.getTargetClass() + "]: " +  
  64.     "Common causes of this problem include using a final class or a  
  65.    non-visible class",ex);  
  66.  }  
  67.  catch (IllegalArgumentException ex) {  
  68.   throw new AopConfigException("Could not generate CGLIB subclass of class [" +  
  69.     this.advised.getTargetClass() + "]: " +  
  70.     "Common causes of this problem include using a final class or a  
  71.    non-visible class",ex);  
  72.  }  
  73.  catch (Exception ex) {  
  74.   // TargetSource.getTarget() failed。  
  75.   throw new AopConfigException("Unexpected AOP exception", ex);  
  76.  }  
  77. }  

 

这样,通过使用AopProxy对象封装target目标对象之后,通过ProxyFactoryBean的getObject方法得到的对象就不是一个普通的Java对象了,而是一个AopProxy代理对象。在ProxyFactoryBean中配置的target目标对象,这个时候已经不会直接暴露给应用,而是作为AOP实现的一部分。对target目标对象的方法调用会首先被AopProxy代理对象拦截,对于不同的AopProxy代理对象生成方式,会使用不同的拦截回调入口。例如,对于JDK的AopProxy代理对象,使用的是InvocationHandler的invoke回调入口;而对于CGLIB的AopProxy代理对象,使用的是设置好的callback回调,这是由对CGLIB的使用来决定的;在这些callback回调中,对于AOP实现,是通过DynamicAdvisedInterceptor来完成的,而DynamicAdvisedInterceptor的回调入口是intercept方法。通过这一系列的准备,已经为实现AOP的横切机制奠定了基础,在这个基础上,AOP的Advisor已经可以通过AopProxy代理对象的拦截机制,对需要它进行增强的target目标对象发挥切面的强大威力了。


可以把AOP的实现部分看成由基础设施准备和AOP运行辅助这两个部分组成。这里的AopProxy代理对象的生成,可以看成是一个静态的AOP基础设施的建立过程。通过这个准备过程把代理对象、拦截器这些待调用的部分都准备好,等待着AOP运行过程中对这些基础设施的使用。对于应用触发的AOP应用,会涉及AOP框架的运行和对AOP基础设施的使用。这些动态的运行部分,是从我们前面提到的拦截器回调入口开始的,这些拦截器调用的实现原理和AopProxy代理对象生成一样,也是AOP实现的重要组成部分,同时也是我们下面要重点分析的内容,让我们继续深入到AopProxy代理对象的回调实现中去看一看,慢慢地揭开Spring AOP实现的另一层神秘的面纱。


3.3  Spring AOP拦截器调用的实现


3.3.1  JdkDynamicAopProxy的invoke拦截
上面我们看到了在Spring中通过ProxyFactoryBean实现AOP功能的第一步,得到AopProxy代理对象的基本过程,以及通过使用JDK和CGLIB最终产生AopProxy代理对象的实现原理。下面我们看看AopProxy代理对象的拦截机制是怎样发挥作用和实现AOP功能的。在JdkDynamicAopProxy中生成Proxy对象时,我们回顾一下它的AopProxy代理对象的生成调用,如下所示。

Java代码  收藏代码
  1. Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);  

 

这里的this参数对应的是InvocationHandler对象,InvocationHandler是JDK定义的反射类的一个接口,这个接口定义了invoke方法,而这个invoke方法是作为JDKProxy代理对象进行拦截的回调入口出现的。我们看到,在JdkDynamicAopProxy实现了InvocationHandler接口,也就是说当Proxy对象的代理方法被调用时,JdkDynamicAopProxy的invoke方法作为Proxy对象的回调函数而被触发,从而通过invoke的具体实现,来完成对目标对象方法调用的拦截或者说功能增强的工作。下面我们看看JdkDynamicAopProxy的invoke方法实现,如代码清单3-18所示。可以看到,对Proxy对象完成的代理设置是在invoke方法中完成的,这些设置包括获取目标对象、拦截器链,同时把这些对象作为输入,创建了ReflectiveMethodInvocation对象,通过这个ReflectiveMethodInvocation对象来完成对AOP功能实现的封装。在这个invoke方法中,包含了一个完整的拦截器链对目标对象的拦截过程,比如获得拦截器链并对其中的拦截器进行配置,逐个运行拦截器链里的拦截增强,直到最后对目标对象方法的运行,等等。


代码清单3-18  AopProxy代理对象的回调

 

Java代码  收藏代码
  1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  2.  MethodInvocation invocation = null;  
  3.  Object oldProxy = null;  
  4.  boolean setProxyContext = false;  
  5.  TargetSource targetSource = this.advised.targetSource;  
  6.  Class targetClass = null;  
  7.  Object target = null;  
  8.  try {  
  9.   if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {  
  10.    // The target does not implement the equals(Object) method itself.  
  11.    return equals(args[0]);  
  12.   }  
  13.   if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {  
  14.    // The target does not implement the hashCode() method itself.  
  15.    return hashCode();  
  16.   }  
  17.   if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&  
  18.     method.getDeclaringClass().isAssignableFrom(Advised.class)) {  
  19.    // Service invocations on ProxyConfig with the proxy config...  
  20.    return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);  
  21.   }  
  22.   Object retVal = null;  
  23.   if (this.advised.exposeProxy) {  
  24.    // Make invocation available if necessary.  
  25.    oldProxy = AopContext.setCurrentProxy(proxy);  
  26.    setProxyContext = true;  
  27.   }  
  28.   /** 
  29.   * May be null. Get as late as possible to minimize the time we "own" the 
  30.   * target, in case it comes from a pool. 
  31.   */  
  32.   //得到目标对象的地方。  
  33.   target = targetSource.getTarget();  
  34.   if (target != null) {  
  35.    targetClass = target.getClass();  
  36.   }  
  37.   // Get the interception chain for this method.  
  38.   // 这里获得定义好的拦截器链。  
  39.   List<Object> chain = this.advised.getInterceptorsAndDynamicInterception  
  40. Advice(method, targetClass);  
  41.   /** 
  42.   * Check whether we have any advice. If we don't, we can fallback on 
  43.   * direct reflective invocation of the target, and avoid creating a MethodInvocation. 
  44.   */  
  45.   
  46.   // 如果没有设定拦截器,那么我们就直接调用target的对应方法。  
  47.   if (chain.isEmpty()) {  
  48.    /** 
  49.    * We can skip creating a MethodInvocation: just invoke the target directly 
  50.    * Note that the final invoker must be an InvokerInterceptor so we 
  51.    * know it does nothing but a reflective operation on the target, and no hot 
  52.   * swapping or fancy proxying. 
  53.    */  
  54.    retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);  
  55.   }  
  56.   else {  
  57.    // We need to create a method invocation...  
  58.    /** 
  59.    * 如果有拦截器的设定,那么需要调用拦截器之后才调用目标对象的相应方法, 
  60.    * 通过构造一个ReflectiveMethodInvocation来实现,下面我们会看 
  61.    * 这个ReflectiveMethodInvocation类的具体实现。 
  62.    */  
  63.    invocation = new ReflectiveMethodInvocation(proxy, target, method,  
  64.   args, targetClass, chain);  
  65.    // Proceed to the joinpoint through the interceptor chain.  
  66.    //沿着拦截器链继续前进。  
  67.    retVal = invocation.proceed();  
  68.   }  
  69.   
  70.   // Massage return value if necessary.  
  71.   if (retVal != null && retVal == target && method.getReturnType().  
  72.  isInstance(Proxy) && !RawTargetAccess.class.isAssignableFrom  
  73.     (method.getDeclaringClass())) {  
  74.    /** 
  75.    * Special case: it returned "this" and the return type of the method 
  76.    * is type-compatible. Note that we can't help if the target sets 
  77.    * a reference to itself in another returned object. 
  78.    */  
  79.    retVal = Proxy;  
  80.   }  
  81.   return retVal;  
  82.  }  
  83.  finally {  
  84.   if (target != null && !targetSource.isStatic()) {  
  85.    // Must have come from TargetSource.  
  86.    targetSource.releaseTarget(target);  
  87.   }  
  88.   if (setProxyContext) {  
  89.    // Restore old proxy.  
  90.    AopContext.setCurrentProxy(oldProxy);  
  91.   }  
  92.  }  
  93. }  

 

3.3.2  Cglib2AopProxy的intercept拦截
在分析Cglib2AopProxy的AopProxy代理对象生成的时候,我们知道对于AOP的拦截调用,其回调是在DynamicAdvisedInterceptor对象中实现的,这个回调的实现在intercept方法中,如代码清单3-19所示。Cglib2AopProxy的intercept回调方法的实现和JdkDynamicAopProxy的回调实现是非常类似的,只是在Cglib2AopProxy中构造的是CglibMethodInvocation对象来完成拦截器链的调用,而在JdkDynamicAopProxy中是通过构造ReflectiveMethodInvocation对象来完成这个功能的。


代码清单3-19  DynamicAdvisedInterceptor的intercept

Java代码  收藏代码
  1. public Object intercept(Object proxy, Method method, Object[] args, MethodProxy  
  2. methodProxy) throws Throwable {  
  3.  Object oldProxy = null;  
  4.  boolean setProxyContext = false;  
  5.  Class targetClass = null;  
  6.  Object target = null;  
  7.  try {  
  8.   if (this.advised.exposeProxy) {  
  9.    // Make invocation available if necessary.  
  10.    oldProxy = AopContext.setCurrentProxy(proxy);  
  11.    setProxyContext = true;  
  12.   }  
  13.   /** 
  14.   * May be <code>null</code>. Get as late as possible to minimize the time we 
  15.   * "own" the target, in case it comes from a pool. 
  16.   */  
  17.   target = getTarget();  
  18.   if (target != null) {  
  19.    targetClass = target.getClass();  
  20.   }  
  21.   //从advised中取得配置好的AOP通知。  
  22.   List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice  
  23.  (method, targetClass);  
  24.   Object retVal = null;  
  25.   /** 
  26.   * Check whether we only have one InvokerInterceptor: that is, 
  27.   * no real advice, but just reflective invocation of the target. 
  28.   */  
  29.   // 如果没有AOP通知配置,那么直接调用target对象的调用方法。  
  30.   if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {  
  31.    /** 
  32.    * We can skip creating a MethodInvocation: just invoke the target directly. 
  33.    * Note that the final invoker must be an InvokerInterceptor, so we know 
  34.    * it does nothing but a reflective operation on the target, and no hot 
  35.    * swapping or fancy proxying. 
  36.    */  
  37.    retVal = methodProxy.invoke(target, args);  
  38.   }  
  39.   else {  
  40.    //通过CglibMethodInvocation来启动advice通知。  
  41.    retVal = new CglibMethodInvocation(proxy, target, method, args,  
  42.   targetClass, chain, methodProxy).proceed();  
  43.   }  
  44.   retVal = massageReturnTypeIfNecessary(proxy, target, method, retVal);  
  45.   return retVal;  
  46.  }  
  47.  finally {  
  48.   if (target != null) {  
  49.    releaseTarget(target);  
  50.   }  
  51.   if (setProxyContext) {  
  52.    // Restore old proxy.  
  53.    AopContext.setCurrentProxy(oldProxy);  
  54.   }  
  55.  }  
  56. }  

 

3.3.3  目标对象方法的调用
如果没有设置拦截器,那么会对目标对象的方法直接进行调用,对于JdkDynamicAopProxy代理对象,这个对目标对象的方法调用是通过AopUtils使用反射机制完成的,在AopUtils.
invokeJoinpointUsingReflection的方法中实现,如代码清单3-20所示。在这个调用中,首先得到调用方法的反射对象,然后使用invoke启动对方法反射对象的调用。

代码清单3-20  使用反射完成目标对象的方法调用

Java代码  收藏代码
  1. public static Object invokeJoinpointUsingReflection(Object target, Method method,  
  2. Object[] args)  
  3.  throws Throwable {  
  4.  // Use reflection to invoke the method.  
  5.  // 这里是使用反射调用target对象方法的地方。  
  6.  try {  
  7.   ReflectionUtils.makeAccessible(method);  
  8.   return method.invoke(target, args);  
  9.  }  
  10.  catch (InvocationTargetException ex) {  
  11.   // Invoked method threw a checked exception.  
  12.   // We must rethrow it. The client won't see the interceptor.  
  13.   throw ex.getTargetException();  
  14.  }  
  15.  catch (IllegalArgumentException ex) {  
  16.   throw new AopInvocationException("AOP configuration seems to be invalid:  
  17.  tried calling method [" + method + "] on target [" + target + "]", ex);  
  18.  }  
  19.  catch (IllegalAccessException ex) {  
  20.   throw new AopInvocationException("Could not access method [" + method +  
  21.  "]", ex);  
  22.  }  
  23. }  

 

对于使用Cglib2AopProxy的代理对象,它对目标对象的调用是通过CGLIB的MethodProxy对象来直接完成的,这个对象的使用是由CGLIB的设计来决定的;对于具体的调用在DynamicAdvisedInterceptor的intercept方法中可以看到,使用的是CGLIB封装好的功能,相对JdkDynamicAopProxy的实现来说,形式上看起来较为简单,但它们的功能却都是一样的,都是完成对目标对象方法的调用,具体的代码实现如下。

Java代码  收藏代码
  1. retVal = methodProxy.invoke(target, args);  

3.1  Spring AOP概述


3.1.1  AOP概念回顾
AOP是Aspect-Oriented Programming(面向方面编程)的简称,维基百科对它的解释如下所示。


维基百科对“AOP”相关概念的叙述
Aspect是一种新的模块化机制,用来描述分散在对象、类或函数中的横切关注点(crosscutting concern)。从关注点中分离出横切关注点是面向侧面的程序设计的核心概念。分离关注点使得解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑的代码中不再含有针对特定领域问题代码的调用,业务逻辑同特定领域问题的关系通过侧面来封装、维护,这样原本分散在整个应用程序中的变动就可以很好地管理起来。


这里提到的概念是从模块化出发的,面向对象设计其实也是一种模块化的方法,它把相关的数据及其处理方法放在了一起,与单纯的使用子函数进行封装相比,面向对象的模块化特性更完备,它体现了计算的一个基本原则——让计算尽可能地靠近数据。这样一来,代码组织起来就更加整齐和清晰,一个类就是一个基本的模块。很多程序的功能还可以通过设计类的继承关系而得到重用,进一步提高了开发效率。后来又出现了各种各样的设计模式供我们使用,让我们在设计程序功能时更加得心应手。


虽然我们利用面向对象的方法可以很好地组织代码,也可以通过继承关系实现代码重用,但是程序中总是会出现一些重复的代码,而且不太方便使用继承的方法把它们重用和管理起来。它们功能重复并且需要作用在不同的地方,虽然可以对这些代码做一些简单的封装,使之成为一些公共函数,但是在这种显式的调用中,使用它们并不是很方便。例如,这个公共函数在什么情况下可以使用,能不能更灵活地使用,等等。


在使用这些公共函数时,往往也需要进行一些逻辑设计。也就是需要代码实现来支持,而这些逻辑代码也是需要维护的。这时就是AOP大显身手的时候了,使用AOP后,不仅可以将这些重复的代码抽取出来单独维护,在需要使用时统一调用这些公共代码,还可以为如何使用这些公共代码提供丰富灵活的手段。这虽然与设计公共子模块有几分相似,但在传统的公共子模块调用中,除了直接硬调用之外并没有其他的手段,而AOP为处理这一类问题提供了一套完整的理论和灵活多样的实现方法。也就是说,通过AOP提出横切的概念以后,在把模块功能正交化的同时,也在此基础上提供了一系列横切的灵活实现。比如通过使用Proxy代理对象、拦截器、字节码翻译技术等,通过这一系列已有的AOP或者AOP实现技术,来实现切面应用的各种编织实现和环绕增强;为了更好地应用AOP技术,技术专家们还成立了AOP联盟来探讨AOP的标准化,有了这些支持,AOP的发展就更快了。关于AOP技术,可以到AOP联盟的文档里找到一些相关的介绍,从而提高我们对AOP的理解。比如在AOP联盟的网站上提到的以下AOP技术:


* AspectJ:源代码和字节码级别的编织器,用户需要使用不同于Java的新语言。
* AspectWerkz:AOP框架,使用字节码动态编织器和XML配置。
* JBoss-AOP:基于拦截器和元数据的AOP框架,运行在JBoss应用服务器上。

 

以及在AOP中使用到的一些相关的技术实现:


* BCEL(Byte-Code Engineering Library):Java字节码操作类库,具体的信息可以参见项目网站:http://jakarta.apache.org/bcel/
* Javassist:Java字节码操作类库,JBoss的一个子项目,项目信息可以参见项目网站:http://jboss.org/javassist/


对应于现有的AOP实现方案,AOP联盟对它们进行了一定程度的抽象,从而定义出AOP体系结构,结合这个体系结构去了解AOP技术,对我们理解AOP的概念是非常有帮助的,这个AOP体系结构如图3-1所示。



 

图3-1  AOP联盟定义的AOP体系结构


AOP联盟定义的AOP体系结构把与AOP相关的概念大致分为了由高到低、从使用到实现的三个层次。从上往下,最高层是语言和开发环境,在这个环境中可以看到几个重要的概念:基础可以视为待增强对象或者说目标对象;切面通常包含对于基础的增强应用;配置可以看成是一种编织或者说配置,通过在AOP体系中提供这个配置环境,可以把基础和切面结合起来,从而完成切面对目标对象的编织实现。


在Spring AOP实现中,是使用Java语言来实现增强对象与切面增强应用的,并为这两者的结合提供了配置环境。对于编织配置,可以使用IoC容器来完成;对于POJO对象的配置,本来就是Spring的核心IoC容器的强项。因此,对于使用Spring的AOP开发而言,使用POJO就能完成AOP任务。但是,对于其他的AOP实现方案,可能需要使用特定的实现语言、配置环境甚至是特定的编译环境。例如在AspectJ中,尽管切面增强的对象是Java对象,但却需要使用特定的Aspect语言和AspectJ编译器。AOP体系结构的第二个层次是为语言和开发环境提支持的,在这个层次中可以看到AOP框架的高层实现,主要包括配置和编织实现两部分内容。例如配置逻辑和编织逻辑实现本身,以及对这些实现进行抽象的一些高层API封装。这些实现和API封装,为前面提到的语言和开发环境的实现提供了有力的支持。


最底层是编织的具体实现模块,图3-1中看到的各种技术,都可以作为编织逻辑的具体实现方法,比如反射、程序处理、拦截器框架、类装载框架、元数据处理等。阅读完本章对Spring AOP实现原理的分析,我们会了解到,在Spring AOP中,使用的是Java本身的语言特性,比如Java Proxy代理类、拦截器这些技术,来完成AOP编织的实现。


对Spring平台或者说生态系统来说,AOP是Spring框架的核心功能模块之一。AOP与IoC容器的结合使用, 为应用开发或者Spring自身功能的扩展都提供了许多便利。

 

Spring AOP的实现和其他特性的实现一样,除了可以使用Spring本身提供的AOP实现之外,还封装了业界优秀的AOP解决方案AspectJ来让应用使用。在本章中,我们主要对Spring自身的AOP实现原理进行分析;在这个AOP实现中,Spring充分利用了IoC容器Proxy代理对象以及AOP拦截器的功能特性,通过这些对AOP基本功能的封装机制,为用户提供了AOP的实现框架。所以,要了解这些AOP的基本实现,需要我们对Java 的Proxy机制有一些基本了解。在Spring中,有一些相关的概念与AOP设计相对应;在本章的内容中,按照笔者个人的粗浅理解,我们会结合Spring的AOP实现,温故而知新,在以下的小节中对一些相关的AOP概念,先简单地回顾一下;然后再逐步展开对AOP实现原理的分析,包在这些实现原理的分析中,包括了代理对象的生成,AOP拦截器的实现,等等。这里以ProxyFactoryBean和ProxyFactory为例子来进行说明。通过分析它们背后隐藏的实现原理来了解Spring的AOP模块。和前面一样,我们需要在Eclipse环境中导入对应的AOP代码模块来辅助这些原理实现的分析,具体需要导入的包是:org.springframework.aop,这个代码包里包含了Spring AOP的实现代码。


3.1.2  Advice通知
Advice(通知):定义在连接点做什么,为切面增强提供织入接口。在Spring AOP中,它主要描述Spring AOP围绕方法调用而注入的切面行为。Advice是AOP联盟定义的一个接口,具体的接口定义在org.aopalliance.aop.Advice中。在Spring AOP的实现中,使用了这个统一接口,并通过这个接口为AOP切面增强的织入功能做了更多的细化和扩展,比如提供了更具体的通知类型,像BeforeAdvice、AfterAdvice、ThrowAdvice等。作为Spring AOP定义的接口类,具体的切面增强可以通过这些接口集成到AOP框架中去发挥作用。对于这些接口类,我们下面会进行逐一详细地讨论,我们从接口BeforeAdvice开始,首先了解它的类层次关系,如图3-2所示。



 

图3-2  BeforeAdvice的类层次关系


在BeforeAdvice的继承关系中,定义了为待增强的目标方法设置的前置增强接口MethodBeforeAdvice,使用这个前置接口需要实现一个回调函数:

Java代码  收藏代码
  1. void before(Method method, Object[] args, Object target) throws Throwable;  

 

作为回调函数,before方法的实现在advice中被配置到目标方法后,会在调用目标方法时被回调。具体的调用参数有:Method对象,这个参数是目标方法的反射对象;object[]对象数组,这个对象数组中包含的是目标方法的输入参数。以CountingBeforeAdvice为例,用它来说明BeforeAdvice的具体使用,CountingBeforeAdvice是接口MethodBeforeAdvice的具体实现,如代码清单3-1所示,它的实现比较简单,完成的工作是统计被调用的方法次数。作为切面增强实现,它会根据调用方法的方法名进行统计,把统计结果根据方法名和调用次数作为键值对放入一个map中。


代码清单3-1  CountingBeforeAdvice的实现

Java代码  收藏代码
  1. public class CountingBeforeAdvice extends MethodCounter implements MethodBeforeAdvice {  
  2. //实现before回调接口,这是接口MethodBeforeAdvice的要求。  
  3. public void before(Method m, Object[] args, Object target) throws Throwable {  
  4.  count(m);  
  5. }  
  6. }  

 

这里调用了count方法,使用了目标方法的反射对象作为参数,完成对调用方法名的统计工作。count方法在CountingBeforeAdvice的基类MethodCounter中实现,如代码清单3-2所示。这个切面增强完成的统计实现并不复杂,它在对象中维护一个哈希表,用来存储统计数据。在统计过程中,首先通过目标方法的反射对象得到方法名,然后进行累加,把统计结果放到维护的哈希表中。如果需要统计数据,就到这个哈希表中根据key来获取。


代码清单3-2  MethodCounter实现统计目标方法调用次数

Java代码  收藏代码
  1. public class MethodCounter implements Serializable {  
  2. // 这个Hashmap用来存储方法名和调用次数的键值对。  
  3. private HashMap<String, Integer> map = new HashMap<String, Integer>();  
  4. //所有的调用次数,不管是什么方法名。  
  5. private int allCount;  
  6. //CountingBeforeAdvice的调用入口。  
  7. protected void count(Method m) {  
  8.  count(m.getName());  
  9. }  
  10. //根据目标方法的方法名统计调用次数。  
  11. protected void count(String methodName) {  
  12.  Integer i = map.get(methodName);  
  13.  i = (i != null) ? new Integer(i.intValue() + 1) : new Integer(1);  
  14.  map.put(methodName, i);  
  15.  ++allCount;  
  16. }  
  17. //根据方法名取得调用的次数。  
  18. public int getCalls(String methodName) {  
  19.  Integer i = map.get(methodName);  
  20.  return (i != null ? i.intValue() : 0);  
  21. }  
  22. //取得所有的方法调用次数。  
  23. public int getCalls() {  
  24.  return allCount;  
  25. }  
  26. public boolean equals(Object other) {  
  27.  return (other != null && other.getClass() == this.getClass());  
  28. }  
  29.   
  30. public int hashCode() {  
  31.  return getClass().hashCode();  
  32. }  
  33. }  

 

在Advice的实现体系中,Spring还提供了AfterAdvice的这种通知类型,它的类接口关系如图3-3所示。



 

图3-3  AfterAdvice的接口关系


在如图3-3所示的AfterAdvice类接口关系中,有一系列对AfterAdvice的实现和接口扩展,比如AfterReturnningAdvice。我们以AfterReturningAdvice通知的实现为例,分析一下AfterAdvice通知类型的实现原理。在AfterReturningAdvice接口中定义了接口方法,如下所示。

 

Java代码  收藏代码
  1. void afterReturning(Object returnValue, Method method, Object[] args, Object  
  2. target) throws Throwable;  

 

AfterReturing方法也是一个回调函数,AOP应用需要在这个接口实现中,提供切面增强的具体设计,在这个Advice通知被正确配置以后,在目标方法调用结束并成功返回的时候,接口实现会被Spring AOP回调。对于回调参数,有目标方法的返回结果、反射对象以及调用参数(AOP把这些参数都封装在一个对象数组中传递进来)等。与前面分析BeforeAdvice一样,在Spring AOP的包中,同样可以看到一个CountingAfterReturningAdvice,作为熟悉AfterRetur-
ningAdvice使用的例子;它的实现基本上与CountingBeforeAdvice是一样的,如代码清单3-3所示。


代码清单3-3  CountingAfterReturningAdvice的实现

Java代码  收藏代码
  1. public class CountingAfterReturningAdvice extends MethodCounter implements  
  2. AfterReturningAdvice {  
  3. public void afterReturning(Object o, Method m, Object[] args, Object target)  
  4. throws Throwable {  
  5.  count(m);  
  6. }  
  7. }  

 

在实现AfterReturningAdvice的接口方法AfterReturning中,可以调用MethodCounter的count方法,从而完成根据方法名来对目标方法调用的次数进行统计。

 

count方法调用的实现与CountingBeforeAdvice基本上是一样的,所不同的是调用发生的时间。尽管增强逻辑相同,但是,如果它实现不同的AOP通知接口,就会被AOP编织到不同的调用场合中。尽管它们完成的增强行为是一样的,都是根据目标方法名对调用次数进行统计,但是它们的最终实现却有很大不同,一个是在目标方法调用前实现切面增强,一个是在目标方法成功调用返回结果后实现切面增强。由此可见,AOP技术给应用带来的灵活性,使得相同的代码完全可以根据应用的需要灵活地出现在不同的应用场合。


了解了BeforeAdvice和AfterAdvice,在Spring AOP中还可以看到另外一种Advice通知类型——ThrowsAdvice,它的类层次关系如图3-4所示。



 

图3-4  ThrowsAdvice的类层次关系


ThrowsAdvice并没有指定需要实现的接口方法,它在抛出异常时被回调,这个回调是AOP使用反射机制来完成的。我们可以通过CountingThrowsAdvice了解ThrowsAdvice的使用方法,如代码清单3-4所示。


代码清单3-4  CountingThrowsAdvice的实现

Java代码  收藏代码
  1. public static class CountingThrowsAdvice extends MethodCounter implements ThrowsAdvice {  
  2.  public void afterThrowing(IOException ex) throws Throwable {  
  3.   count(IOException.class.getName());  
  4.  }  
  5.   
  6.  public void afterThrowing(UncheckedException ex) throws Throwable {  
  7.   count(UncheckedException.class.getName());  
  8.  }  
  9. }  

 

在AfterThrowing方法中,从输入的异常对象中得到异常的名字并进行统计。count方法同样是在MethodCounter中实现,与前面的两个Advice的实现相同,只是CountingBeforeAdvice和CountingAfterReturningAdvice统计的是目标方法的调用次数。在这里,count方法完成的是根据异常名称统计抛出异常的次数。


3.1.3  Pointcut切点
Pointcut(切点):决定Advice通知应该作用于哪个连接点,也就是说通过Pointcut切点来定义需要增强的方法的集合,这些集合的选取可以按照一定的规则来完成。在这种情况下,Pointcut通常意味着标识方法,例如,这些需要增强的地方可以是被某个正则表达式进行标识,或根据某个方法名进行匹配等。


为了方便用户使用,Spring AOP提供了具体的切点供用户使用,关于切点,可以看看Spring AOP中的类继承体系,如图3-5所示。



 

图3-5  Spring AOP的Pointcut类继承关系


在Pointcut的基本接口定义中可以看到,需要返回一个MethodMatcher;对于Point的匹配判断功能,具体是由这个返回的MethodMatcher来完成的,也就是说,由这个MethodMatcher来判断是否需要对当前方法调用进行增强,或者说对当前调用方法是否需要应用配置好的advice通知。在Pointcut的类继承关系中,我们以正则表达式切点 JdkRegexpMethodPointcut的实现原理为例,来具体了解切点Pointcut的工作原理。JdkRegexpMethodPointcut类完成通过正则表达式对方法名进行匹配的功能。在JdkRegexpMethodPointcut的基类 StaticMethodMatcherPointcut的实现中可以看到,设置的MethodMatcher为StaticMethodMatcher,同时JdkRegexpMethodPointcut也是这个MethodMatcher的子类,它的类层次关系如图3-6所示。



 

图3-6  StaticMethodMatcher的类层次关系


如图3-6所示,在Pointcut中,通过这样的类继承关系,MethodMatcher对象实际上是可以被配置成JdkRegexpMethodPointcut来完成方法的匹配判断的。在JdkRegexpMethod-
Pointcut中,可以看到matches方法,这个方法是MethodMatcher定义的接口方法;在JdkRegexpMethodPointcut的实现中,这个matches方法就是使用正则表达式来对方法名进行匹配的地方。关于在AOP框架中对matches方法的调用,我们会在下面具体分析Spring AOP实现中看到。要了解matches在AOP框架中的调用位置,比较简单的方法就是从matches方法作为起始点,去对它的方法调用关系进行追溯,可以看到对matches方法的调用关系如图3-7所示。



 

图3-7  对JdkRegexpMethodPointcut的matches方法的调用关系


在对matches方法的调用关系中可以看到,是JdkDynamicAopProxy的invoke方法中触发了对matches方法的调用,熟悉Proxy使用的读者,一定会想到,这个invoke方法应该就是Proxy对象进行代理回调的入口方法,这个invoke回调的实现是使用JDK动态代理完成AOP功能的一部分,关于这部分实现原理,在下面AOP的实现分析中有详细的阐述。我们这里的重点是了解Pointcut的实现原理,比如去具体看看matches本身的实现,对于JdkRegexpMethod-
Pointcut的matches方法,它的是实现如代码清单3-5所示。


代码清单3-5  JdkRegexpMethodPointcut使用matches完成匹配

Java代码  收藏代码
  1. protected boolean matches(String pattern, int patternIndex) {  
  2.  Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern);  
  3.  return matcher.matches();  
  4. }  

 

在JDKRegexpMethodPointcut中,通过JDK来实现正则表达式的匹配,在代码清单3-5中可以看得很清楚;如果要详细了解使用JDK的正则表达式匹配功能,可以参考JDK的API文档。我们接着看看其他的Pointcut。


在Spring AOP中,还提供了其他的MethodPointcut,比如通过方法名匹配进行Advice匹配的NameMatchMethodPointcut。它的matches方法实现很简单,匹配的条件是方法名相同或者方法名相匹配,如代码清单3-6所示。


代码清单3-6  NameMatchMethodPointcut的matches

Java代码  收藏代码
  1. public boolean matches(Method method, Class targetClass) {  
  2.  for (String mappedName : this.mappedNames) {  
  3.   if (mappedName.equals(method.getName()) || isMatch(method.getName(),  
  4.  mappedName)) {  
  5.    return true;  
  6.   }  
  7.  }  
  8.  return false;  
  9. }  
  10. protected boolean isMatch(String methodName, String mappedName) {  
  11.  return PatternMatchUtils.simpleMatch(mappedName, methodName);  
  12. }  

 
3.1.4  Advisor通知器
Advisor(通知器):当我们完成对目标方法的切面增强设计(advice)和关注点的设计(pointcut)以后,需要一个对象把它们结合起来,完成这个作用的就是Advisor。通过Advisor,可以定义应该使用哪个Advice并在哪个Pointcut使用它,也就是说通过Advisor把Advice和Pointcut结合起来了,这个结合为应用使用IoC容器配置AOP应用,或者说即开即用地使用AOP基础设施。在Spring AOP中,我们举一个Advisor的实现(DefaultPointcutAdvisor)作为例子,去了解Advisor的工作原理。在DefaultPointcutAdvisor中有两个属性,分别是Advice和Pointcut,通过这两个属性,可以分别配置Advice和Pointcut,DefaultPointcutAdvisor的实现如代码清单3-7所示。


代码清单3-7  DefaultPointcutAdvisor的实现

Java代码  收藏代码
  1. public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor  
  2. implements Serializable {  
  3.   
  4. private Pointcut pointcut = Pointcut.TRUE;  
  5. public DefaultPointcutAdvisor() {  
  6. }  
  7. public DefaultPointcutAdvisor(Advice advice) {  
  8.  this(Pointcut.TRUE, advice);  
  9. }  
  10. public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {  
  11.  this.pointcut = pointcut;  
  12.  setAdvice(advice);  
  13. }  
  14. public void setPointcut(Pointcut pointcut) {  
  15.  this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);  
  16. }  
  17. public Pointcut getPointcut() {  
  18.  return this.pointcut;  
  19. }  
  20. public String toString() {  
  21.  return getClass().getName() + ": pointcut [" + getPointcut() + "]; advice ["  
  22. + getAdvice() + "]";  
  23. }  
  24. }  

 

在DefaultPointcutAdvisor中默认的Pointcut被设置为Pointcut.True,这个Pointcut.True是在Pointcut接口中被定为的:

Java代码  收藏代码
  1. Pointcut TRUE = TruePointcut.INSTANCE;  

 

TruePointcut的INSTANCE是一个单件。在它的实现中,可以看到单件模式的具体应用和它的典型使用方法,比如使用static类变量来持有单件实例,使用private私有构造函数来确保除了在当前单件实现中,单件不会再次被创建和实例化,从而保证它的“单件”特性。在TruePointcut的methodMatcher实现中,使用的是TrueMethodMatcher作为方法匹配器。在这个方法匹配器中,它对任何的方法匹配要求都返回true的结果,也就是说对任何方法名的匹配要求,它都会返回匹配成功的结果,和TruePointcut一样,这个TrueMethodMatcher也是一个单件实现。TruePointcut和TrueMethodMatcher的实现如代码清单3-8和代码清单3-9所示。


代码清单3-8  TruePointcut的实现

Java代码  收藏代码
  1. class TruePointcut implements Pointcut, Serializable {  
  2. public static final TruePointcut INSTANCE = new TruePointcut();  
  3. // Enforce Singleton pattern.  
  4. private TruePointcut() {  
  5. }  
  6. public ClassFilter getClassFilter() {  
  7.  return ClassFilter.TRUE;  
  8. }  
  9. public MethodMatcher getMethodMatcher() {  
  10.  return MethodMatcher.TRUE;  
  11. }  

 

代码清单3-9  TrueMethodMatcher的实现

Java代码  收藏代码
  1. class TrueMethodMatcher implements MethodMatcher, Serializable {  
  2. public static final TrueMethodMatcher INSTANCE = new TrueMethodMatcher();  
  3. // Enforce Singleton pattern.  
  4.   
  5. private TrueMethodMatcher() {  
  6. }  
  7. public boolean isRuntime() {  
  8.  return false;  
  9. }  
  10. public boolean matches(Method method, Class targetClass) {  
  11.  return true;  
  12. }  
  13. public boolean matches(Method method, Class targetClass, Object[] args) {  
  14.  // Should never be invoked as isRuntime returns false.  
  15.  throw new UnsupportedOperationException();  
  16. }  
  17. private Object readResolve() {  
  18.  return INSTANCE;  
  19. }  
  20.   
  21. public String toString() {  
  22.  return "MethodMatcher.TRUE";  
  23. }  
  24. }  

 


3.2  建立AopProxy代理对象


3.2.1  配置ProxyFactoryBean
从这部分开始,我们进入到Spring AOP的实现部分,在分析Spring AOP的实现原理中,我们主要以ProxyFactoryBean的实现作为例子和实现的基本线索进行分析。很大一个原因是因为ProxyFactoryBean是在Spring IoC环境中,创建AOP应用的最底层方法,也是最灵活的方法,Spring通过它完成了对AOP使用的封装,以它的实现为入口逐层深入,是很好的一条帮助我们理解Spring AOP实现的学习路径。


在了解ProxyFactoryBean的实现之前,我们先简要地了解一下ProxyFactoryBean的配置和使用,在基于XML配置Spring的Bean的时候,往往需要一系列的配置步骤来使用ProxyFactoryBean和AOP,比如以下这些步骤:


1)定义使用的通知器Advisor,这个通知器应该作为一个Bean来定义。很重要的一点,这个通知器的实现定义了需要对目标对象进行增强的切面行为,也就是Advice通知。
2)定义proxyFactoryBean,把它作为另一个Bean来定义,它是封装AOP功能的主要类。在配置ProxyFactoryBean时,需要设定与AOP实现相关的重要属性,比如ProxyInterface、interceptorNames和target等。从属性名称可以看出,interceptorNames属性的值往往设置为需要定义的通知器,因为这些通知器在ProxyFactoryBean的AOP配置下,是通过使用代理对象的拦截器机制起作用的。所以,这里依然沿用了拦截器这个名字,也算是旧瓶装新酒吧。
3)定义target属性,作为target属性注入的Bean,是需要用AOP通知器中的切面应用来增强的对象,也就是前面我们提到的base对象。


有了这些配置,就可以使用ProxyFactoryBean完成AOP的基本功能了。关于配置的例子,如代码清单3-10所示。与前面提到的配置步骤相对应,除了定义了ProxyFactoryBean的AOP封装,还定义了一个Advisor,取名为testAdvisor。作为ProxyFactory配置的一部分,还需要配置拦截的方法调用接口和目标对象。这些基本的配置,是使用ProxyFactoryBean实现AOP功能的重要组成,对于这些配置的实现和作用机制,也是我们后面重点分析的内容。


代码清单3-10  配置ProxyFactoryBean

Java代码  收藏代码
  1. <bean id="testAdvisor" class="com.abc.TestAdvisor"/>  
  2. <bean id="testAOP" class="org.springframework.aop.ProxyFactoryBean>   
  3.  <property name="proxyInterfaces"><value>com.test.AbcInterface"</value></property>  
  4.  <property name="target">  
  5.  <bean class="com.abc.TestTarget"/>  
  6.   </property>  
  7.   <property name="interceptorNames">  
  8.  <list><value> testAdvisor</value></list>  
  9.   </property>  
  10. </bean>  

 

掌握这些配置后,就可以具体看一看这些AOP是如何实现的。也就是说,切面应用是怎样通过ProxyFactoryBean对target对象起作用的。对于这个ProxyFactoryBean,我们先了解一下ProxyFactoryBean的类层次关系,如图3-8所示。在这个类层次关系中,可以看到用来完成AOP应用的类,比如AspectJProxyFactory、ProxyFactory和ProxyFactoryBean,它们都在同一个类的继承体系下,都是ProxyConfig、AdvisedSupport和ProxyCreatorSupport的子类。作为大家的共同基类,ProxyConfig可以看成是一个数据类,这个数据基类为像ProxyFactoryBean这样的子类提供了配置属性。在另一个基类AdvisedSupport的实现中,它封装了AOP中对通知和通知器的相关操作,这些操作对于不同的AOP的代理对象的生成都是一样的,但对于具体的AOP代理对象的创建,AdvisedSupport把它交给它的子类们去完成。ProxyCreatorSupport看成是其子类创建AOP代理对象的一个辅助类,通过继承以上提到的基类的功能实现,具体的AOP代理对象的生成,根据不同的需要分别由ProxyFactoryBean、AspectJProxyFactory和ProxyFactory来完成。对于需要使用AspectJ的AOP应用,AspectJProxyFactory起到集成Spring和AspectJ的作用;对于使用Spring AOP的应用,ProxyFactoryBean和ProxyFactoy都提供了AOP功能的封装,只是如果使用ProxyFactoryBean,可以在IoC容器中完成声明式配置,而使用ProxyFactory,则需要编程式的使用Spring AOP的功能;对于它们是如何封装实现AOP功能的,在本章后面的小节中给出详细的分析,在这里,通过这些类层次关系的了解,希望能先给读者留下一个大致的印象。



 

图3-8  ProxyFactoryBean相关的类层次关系


3.2.2  ProxyFactoryBean生成AopProxy
在Spring AOP的使用中,我们已经了解到,可以通过ProxyFactoryBean来配置目标对象和方面行为。这个ProxyFactoryBean是一个FactoryBean,对FactoryBean这种Spring应用中经常出现的Bean的工作形式,读者一定不会感到陌生,对于FactoryBean的工作原理,我们在结合IoC容器的实现原理分析中已经做过阐述。在ProxyFactoryBean中,通过interceptorNames属性来配置已经定义好的通知器Advisor。虽然这里的名字叫interceptNames,但值得注意的是,实际上却是供AOP应用配置通知器的地方。在ProxyFactoryBean中,需要为target目标对象生成Proxy代理对象,从而为AOP横切面的编织做好准备工作。这些具体的代理对象生成工作,在以后的实现原理分析中,我们可以看到是通过JDK的Proxy或CGLIB来完成的。


在ProxyFactoryBean中,它的AOP实现需要依赖JDK或者CGLIB提供的Proxy特性。从FactoryBean中获取对象,是用getObject()方法作为入口完成的;让我们进入ProxyFactoryBean的实现中去,这个我们一点也不陌生的getObject方法,是FactoryBean需要实现的接口。对ProxyFactoryBean来说,把需要对target目标对象增加的增强处理,都通过getObject方法进行封装了,这些增强处理是为AOP功能的实现提供服务的。对于getObject的实现如代码清单3-11所示。getObject方法首先对通知器链进行初始化,通知器链封装了一系列的拦截器,这些拦截器都要从配置中读取,然后为代理对象的生成做好准备。在生成代理对象的时候,因为Spring中有sigleton类型和prototype类型这两种不同的Bean,所以这里对代理对象的生成需要做一个区分。


代码清单3-11  ProxyFactoryBean的getObject

Java代码  收藏代码
  1. public Object getObject() throws BeansException {  
  2.  //这里初始化通知器链。  
  3.  initializeAdvisorChain();  
  4.  //这里对singleton和protype的类型进行区分,生成对应的proxy。  
  5.  if (isSingleton()) {  
  6.   return getSingletonInstance();  
  7.  }  
  8.  else {  
  9.   if (this.targetName == null) {  
  10.    logger.warn("Using non-singleton proxies with singleton targets is  
  11. often undesirable. " +  
  12.      "Enable prototype proxies by setting the 'targetName' property.");  
  13.   }  
  14.   return newPrototypeInstance();  
  15.  }  
  16. }  

 

 为Proxy代理对象配置Advisor链是在initializeAdvisorChain方法中完成的,如代码清单3-12所示。这个初始化过程有一个标志位advisorChainInitialized,这个标志用来表示通知器链是否已经初始化。如果已经初始化,那么这里就不会再初始化,而是直接返回。也就是说,这个初始化的工作,发生在应用第一次通过ProxyFactoryBean去获取代理对象的时候。在完成这个初始化之后,接着会读取配置中出现的所有通知器,这个取得通知器的过程也比较简单,把通知器的名字交给容器的getBean方法就可以了,这是通过对IoC容器实现的一个回调来完成的。然后把从IoC容器中取得的通知器加入到拦截器链中,这个动作是由addAdvisorOn-
ChainCreation方法来实现的。


代码清单3-12  对Advisor配置链的初始化

Java代码  收藏代码
  1. private synchronized void initializeAdvisorChain() throws AopConfigException,  
  2. BeansException {  
  3.  if (this.advisorChainInitialized) {  
  4.   return;  
  5.  }  
  6.  if (!ObjectUtils.isEmpty(this.interceptorNames)) {  
  7.   if (this.beanFactory == null) {  
  8.    throw new IllegalStateException("No BeanFactory available anymore  
  9.    (probably due to serialization) " +  
  10.    "- cannot resolve interceptor names " + Arrays.asList(this.  
  11.   interceptorNames));  
  12.   }  
  13.   /** 
  14.   * Globals can't be last unless we specified a targetSource 
  15.   * using the property... 
  16.   */  
  17.   if (this.interceptorNames[this.interceptorNames.length - 1].endsWith  
  18.  (GLOBAL_SUFFIX) && this.targetName == null && this.targetSource ==  
  19.  EMPTY_TARGET_  
  20. SOURCE) {  
  21.    throw new AopConfigException("Target required after globals");  
  22.   }  
  23.   
  24.   // Materialize interceptor chain from bean names.  
  25.   // 这里是添加advisor链的调用,是通过interceptorNames属性来进行配置的。  
  26.   for (String name : this.interceptorNames) {  
  27.    if (logger.isTraceEnabled()) {  
  28.     logger.trace("Configuring advisor or advice '" + name + "'");  
  29.    }  
  30.   
  31.    if (name.endsWith(GLOBAL_SUFFIX)) {  
  32.     if (!(this.beanFactory instanceof ListableBeanFactory)) {  
  33.      throw new AopConfigException(  
  34.        "Can only use global advisors or interceptors with  
  35.       a ListableBeanFactory");  
  36.     }  
  37.     addGlobalAdvisor((ListableBeanFactory) this.beanFactory,  
  38.       name.substring(0, name.length() - GLOBAL_SUFFIX.length()));  
  39.    }  
  40.   
  41.    else {  
  42.     // If we get here, we need to add a named interceptor.  
  43.     // We must check if it's a singleton or prototype.  
  44.     Object advice;  
  45.     if (this.singleton || this.beanFactory.isSingleton(name)) {  
  46.      // Add the real Advisor/Advice to the chain.  
  47.      advice = this.beanFactory.getBean(name);  
  48.     }  
  49.     else {  
  50.      // It's a prototype Advice or Advisor: replace with a prototype.  
  51.      /** 
  52.      *Avoid unnecessary creation of prototype bean just for 
  53.      *advisor chain initialization. 
  54.      */  
  55.      advice = new PrototypePlaceholderAdvisor(name);  
  56.     }  
  57.     addAdvisorOnChainCreation(advice, name);  
  58.    }  
  59.   }  
  60.  }  
  61.  this.advisorChainInitialized = true;  
  62. }  

 

生成singleton的代理对象在getSingletonInstance()的代码中完成,这个方法是ProxyFactoryBean生成AopProxy代理对象的调用入口。在代理对象中会封装对target目标对象的调用,也就是说针对target对象的方法调用行为,会被这里生成的代理对象所拦截。具体的生成过程是,首先需要读取ProxyFactoryBean中的配置,为生成代理对象做好必要的准备,比如设置代理的方法调用接口等。对于在getSingletonInstance()方法中代理对象的生成过程,如代码清单3-13所示。


代码清单3-13  生成单件代理对象

Java代码  收藏代码
  1. private synchronized Object getSingletonInstance() {  
  2.  if (this.singletonInstance == null) {  
  3.   this.targetSource = freshTargetSource();  
  4.   if (this.autodetectInterfaces && getProxiedInterfaces().length == 0  
  5.  && !isProxyTargetClass()) {  
  6.    // Rely on AOP infrastructure to tell us what interfaces to proxy.  
  7.    Class targetClass = getTargetClass();  
  8.    if (targetClass == null) {  
  9.     throw new FactoryBeanNotInitializedException("Cannot determine  
  10.    target class for proxy");  
  11.    }  
  12. // 这里设置代理对象的接口。  
  13. setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));  
  14.   }  
  15.   // Initialize the shared singleton instance.  
  16.   super.setFrozen(this.freezeProxy);  
  17.   // 注意这里的方法会使用ProxyFactory来生成我们需要的Proxy。  
  18.   this.singletonInstance = getProxy(createAopProxy());  
  19.  }  
  20.  return this.singletonInstance;  
  21. }  
  22. //使用createAopProxy返回的AopProxy来得到代理对象。  
  23. protected Object getProxy(AopProxy aopProxy) {  
  24.  return aopProxy.getProxy(this.proxyClassLoader);  
  25. }  

 

这里出现了AopProxy类型的对象,Spring使用这个AopProxy接口类把AOP代理对象的实现与框架的其他部分有效地分离开来。AopProxy是一个接口,它有两个子类实现,一个是Cglib2AopProxy,另一个是JdkDynamicProxy。顾名思义,对这两个AopProxy接口的子类实现,Spring分别使用CGLIB和JDK来生成需要的Proxy代理对象。


具体的代理对象的生成,是在ProxyFactoryBean的基类AdvisedSupport的实现中通过AopProxyFactory完成的,这个代理对象要么从JDK中生成,要么借助CGLIB获得。因为ProxyFactoryBean本身就是AdvisedSupport的子类,在ProxyFactoryBean中获得AopProxy是很方便的,具体的AopProxy生成过程,我们可以在ProxyCreatorSupport中看到。至于需要生成什么样的代理对象,信息都封装在AdvisedSupport里,这个对象也是生成AopProxy的方法的输入参数,这里设置为this本身,因为ProxyCreatorSupport本身就是AdvisedSupport的子类。在ProxyCreatorSupport中生成代理对象的入口实现,如代码清单3-14所示。


代码清单3-14  ProxyCreatorSupport生成AopProxy对象

Java代码  收藏代码
  1. protected final synchronized AopProxy createAopProxy() {  
  2.  if (!this.active) {  
  3.   activate();  
  4.  }  
  5.  /** 
  6.  *通过AopProxyFactory取得AopProxy,这个AopProxyFactory是在 
  7.  *初始化函数中定义的,使用的是DefaultAopProxyFactory。 
  8.  */  
  9.  return getAopProxyFactory().createAopProxy(this);  
  10. }  

 

这里使用了AopProxyFactory来创建AopProxy,AopProxyFactory使用的是DefaultAopProxyFactory。这个被使用的AopProxyFactory,作为AopProxy的创建工厂对象,是在ProxyFactoryBean的基类ProxyCreatorSupport中被创建的。在创建AopProxyFactory的时候,它被设置为DefaultAopProxyFactory。很显然,Spring给出了这个默认的AopProxyFactory工厂的实现。有了这个AopProxyFactory对象以后,问题就转换为:在DefaultAopProxyFactory中AopProxy是怎样生成的问题了。


关于AopProxy代理对象的生成,需要考虑使用哪种生成方式,如果目标对象是接口类,那么适合使用JDK来生成代理对象,否则Spring会使用CGLIB来生成目标对象的代理对象。为了满足不同的代理对象生成的要求,DefaultAopProxyFactory作为AopProxy对象的生产工厂,可以根据不同的需要生成这两种AopProxy对象。对于AopProxy对象的生产过程,在DefaultAopProxyFactory中创建AopProxy的代码中可以清楚地看到,但这是一个比较高层次的AopProxy代理对象的生成过程,如代码清单3-15所示。所谓高层次,是指在DefaultAopProxyFactory创建AopProxy的过程中,对不同的AopProxy代理对象的生成,所涉及的生成策略和场景做了相应的设计,但是对于具体的AopProxy代理对象的生成,最终并没有由这个DefaultAopProxyFactory来完成,比如对JDK和CGLIB这些具体的技术的使用,对具体的实现层次的代理对象的生成,是由Spring封装的JdkDynamicAopProxy和CglibProxyFactory类来完成的。


在AopProxy代理对象的生成过程中,首先要从AdvisedSupport对象中取得配置的目标对象,这个目标对象是实现AOP功能所必需的,道理很简单,AOP完成的是切面应用对目标对象的增强,皮之不存,毛将焉附,这个目标对象可以看做是“皮”,而AOP切面增强就是依附于这块皮上的“毛发”。如果这里发现没有配置目标对象的话,会直接抛出异常,提醒AOP应用,需要提供正确的目标对象的配置。在对目标对象配置的检查完成以后,需要根据配置的情况来决定使用什么方式来创建AopProxy代理对象。一般而言,默认的方式是使用JDK来产生AopProxy代理对象,但是如果遇到配置的目标对象不是接口类的实现的时候,会使用CGLIB来产生AopProxy代理对象;在使用CGLIB来产生AopProxy代理对象的时候,因为CGLIB是一个第三方的类库,本身不在JDK的基本类库中,所以需要在CLASSPATH路径中正确地配置,以便能够加载和使用。在Spring中,使用JDK和CGLIB来生成AopProxy代理对象的工作,是由JdkDynamicAopProxy和CglibProxyFactory来完成的。对于详细的代理对象的生成过程,在下面的小节中,将逐个进行详细的分析。


代码清单3-15  在DefaultAopProxyFactory中创建AopProxy

Java代码  收藏代码
  1. public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {  
  2.  if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSupplied  
  3. ProxyInterfaces(config)) {  
  4.   Class targetClass = config.getTargetClass();  
  5.   if (targetClass == null) {  
  6.    throw new AopConfigException("TargetSource cannot determine target  
  7.     class: " +  
  8.    "Either an interface or a target is required for proxy creation.");  
  9.   }//如果targetClass是接口类,使用JDK来生成Proxy。  
  10.   if (targetClass.isInterface()) {  
  11.    return new JdkDynamicAopProxy(config);  
  12.   }  
  13.   if (!cglibAvailable) {  
  14.    throw new AopConfigException(  
  15.      "Cannot proxy target class because CGLIB2 is not available. " +  
  16.      "Add CGLIB to the class path or specify proxy interfaces.");  
  17.   } //如果不是接口类要生成proxy,那么使用cglib来生成。  
  18.   return CglibProxyFactory.createCglibProxy(config);  
  19.  }  
  20.  else {  
  21.   return new JdkDynamicAopProxy(config);  
  22.  }  
  23. }  

 

3.2.3  JDK生成AopProxy代理对象
我们都知道对于AopProxy代理对象,可以由JDK或CGLIB来生成,而JdkDynamicAopProxy和Cglib2AopProxy实现的都是AopProxy接口,它们的层次关系如图3-9所示。


 

 

图3-9  AopProxy接口


我们先看看在AopProxy接口实现中,JdkDynamicAopProxy是怎样完成AopProxy代理对象生成工作的。这个代理对象的生成过程如代码清单3-16所示。在JdkDynamicAopProxy中,使用了JDK的Proxy类来生成代理对象,在生成Proxy对象之前,首先需要从advised对象中取得代理对象的代理接口配置,然后调用Proxy的newProxyInstance方法,最终得到对应的Proxy代理对象;在生成代理对象的时候,需要指明三个参数,分别是类装载器、代理接口和Proxy回调方法所在的对象,这个对象需要实现InvocationHandler接口;InvocationHandler接口定义了invoke方法,提供代理对象的回调入口;对于JdkDynamicAopProxy,它本身实现了InvocationHandler接口和invoke方法,这个invoke方法是Proxy代理对象的回调方法,所以可以使用this来把JdkDynamicAopProxy指派给Proxy对象;也就是说JdkDynamicAopProxy对象本身,在Proxy代理的接口方法被调用时,会促发invoke方法的回调。在这个回调方法里,完成了AOP编织实现的封装。在这里,我们先重点关注AopProxy代理对象的生成,Proxy代理对象的invoke实现,将是我们后面详细分析AOP实现原理的重要部分。


代码清单3-16  JdkDynamicAopProxy生成Proxy代理对象

Java代码  收藏代码
  1. public Object getProxy(ClassLoader classLoader) {  
  2.  if (logger.isDebugEnabled()) {  
  3.   logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.  
  4.       getTargetSource());  
  5.  }  
  6.  Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);  
  7.  findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);  
  8.  //这里调用JDK生成Proxy的地方。  
  9.  return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);  
  10. }  

 

3.2.4  CGLIB生成AopProxy代理对象
在AopProxy接口实现中,可以看到使用CGLIB来生成Proxy代理对象,这个Proxy代理对象的生成,可以在Cglib2AopProxy的代码实现中看到,同样是在AopProxy的接口方法getProxy的实现完成的,如代码清单3-17所示。可以看到具体对CGLIB的使用,比如对Enhancer对象的配置,以及通过Enhancer对象生成代理对象的过程。在这个生成代理对象的过程中,需要注意的是对Enhancer对象callback回调的设置,正是通过这些callback回调,封装了Spring AOP的实现,就像我们在前面看到的JDK的Proxy对象的invoke回调方法一样;在Enhancer的callback回调设置中,实际上是通过设置DynamicAdvisedInterceptor拦截器来完成AOP功能的,如果读者感兴趣的话,可以在getCallbacks方法实现中看到回调DynamicAdvisedInterceptor的设置:

Java代码  收藏代码
  1. Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);  

 

对于这个DynamicAdvisedInterceptor中的回调实现,我们将在后面详细地进行分析。


代码清单3-17  Cglib2AopProxy生成AopProxy代理对象

Java代码  收藏代码
  1. public Object getProxy(ClassLoader classLoader) {  
  2.  if (logger.isDebugEnabled()) {  
  3.   logger.debug("Creating CGLIB2 proxy: target source is " + this.advised.  
  4.  getTargetSource());  
  5.  }  
  6.  //从advised中取得在IoC容器中配置的target对象。  
  7.  try {  
  8.   Class rootClass = this.advised.getTargetClass();  
  9.   Assert.state(rootClass != null, "Target class must be available for  
  10.  creating a CGLIB proxy");  
  11.   Class proxySuperClass = rootClass;  
  12.   if (AopUtils.isCglibProxyClass(rootClass)) {  
  13.    proxySuperClass = rootClass.getSuperclass();  
  14.    Class[] additionalInterfaces = rootClass.getInterfaces();  
  15.    for (Class additionalInterface : additionalInterfaces) {  
  16.     this.advised.addInterface(additionalInterface);  
  17.    }  
  18.   }  
  19.   // Validate the class, writing log messages as necessary.  
  20.   validateClassIfNecessary(proxySuperClass);  
  21.   // Configure CGLIB Enhancer...  
  22.   // 创建并配置CGLIB的Enhancer,这个Enhancer对象是CGLIB的主要操作类。  
  23.   Enhancer enhancer = createEnhancer();  
  24.   if (classLoader != null) {  
  25.    enhancer.setClassLoader(classLoader);  
  26.    if (classLoader instanceof SmartClassLoader &&  
  27.      ((SmartClassLoader) classLoader).isClassReloadable  
  28.        (proxySuperClass)) {  
  29.     enhancer.setUseCache(false);  
  30.    }  
  31.   }  
  32.   /** 
  33.   *设置Enhancer对象,包括设置代理接口,回调方法,回调方法来自 
  34.   *advised的IoC配置,比如使用AOP的DynamicAdvisedInterceptor拦截器。 
  35.   */  
  36.   enhancer.setSuperclass(proxySuperClass);  
  37.   enhancer.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowable  
  38.  Exception.class));  
  39.   enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));  
  40.   enhancer.setInterceptDuringConstruction(false);  
  41.   Callback[] callbacks = getCallbacks(rootClass);  
  42.   enhancer.setCallbacks(callbacks);  
  43.   enhancer.setCallbackFilter(new ProxyCallbackFilter(  
  44.     this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap,  
  45.    this.fixedInterceptorOffset));  
  46.   Class[] types = new Class[callbacks.length];  
  47.   for (int x = 0; x < types.length; x++) {  
  48.    types[x] = callbacks[x].getClass();  
  49.   }  
  50.   enhancer.setCallbackTypes(types);  
  51.   // 通过Enhancer生成代理对象。  
  52.   Object proxy;  
  53.   if (this.constructorArgs != null) {  
  54.    Proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);  
  55.   }  
  56.   else {  
  57.    proxy = enhancer.create();  
  58.   }  
  59.   return proxy;  
  60.  }  
  61.  catch (CodeGenerationException ex) {  
  62.   throw new AopConfigException("Could not generate CGLIB subclass of class [" +  
  63.     this.advised.getTargetClass() + "]: " +  
  64.     "Common causes of this problem include using a final class or a  
  65.    non-visible class",ex);  
  66.  }  
  67.  catch (IllegalArgumentException ex) {  
  68.   throw new AopConfigException("Could not generate CGLIB subclass of class [" +  
  69.     this.advised.getTargetClass() + "]: " +  
  70.     "Common causes of this problem include using a final class or a  
  71.    non-visible class",ex);  
  72.  }  
  73.  catch (Exception ex) {  
  74.   // TargetSource.getTarget() failed。  
  75.   throw new AopConfigException("Unexpected AOP exception", ex);  
  76.  }  
  77. }  

 

这样,通过使用AopProxy对象封装target目标对象之后,通过ProxyFactoryBean的getObject方法得到的对象就不是一个普通的Java对象了,而是一个AopProxy代理对象。在ProxyFactoryBean中配置的target目标对象,这个时候已经不会直接暴露给应用,而是作为AOP实现的一部分。对target目标对象的方法调用会首先被AopProxy代理对象拦截,对于不同的AopProxy代理对象生成方式,会使用不同的拦截回调入口。例如,对于JDK的AopProxy代理对象,使用的是InvocationHandler的invoke回调入口;而对于CGLIB的AopProxy代理对象,使用的是设置好的callback回调,这是由对CGLIB的使用来决定的;在这些callback回调中,对于AOP实现,是通过DynamicAdvisedInterceptor来完成的,而DynamicAdvisedInterceptor的回调入口是intercept方法。通过这一系列的准备,已经为实现AOP的横切机制奠定了基础,在这个基础上,AOP的Advisor已经可以通过AopProxy代理对象的拦截机制,对需要它进行增强的target目标对象发挥切面的强大威力了。


可以把AOP的实现部分看成由基础设施准备和AOP运行辅助这两个部分组成。这里的AopProxy代理对象的生成,可以看成是一个静态的AOP基础设施的建立过程。通过这个准备过程把代理对象、拦截器这些待调用的部分都准备好,等待着AOP运行过程中对这些基础设施的使用。对于应用触发的AOP应用,会涉及AOP框架的运行和对AOP基础设施的使用。这些动态的运行部分,是从我们前面提到的拦截器回调入口开始的,这些拦截器调用的实现原理和AopProxy代理对象生成一样,也是AOP实现的重要组成部分,同时也是我们下面要重点分析的内容,让我们继续深入到AopProxy代理对象的回调实现中去看一看,慢慢地揭开Spring AOP实现的另一层神秘的面纱。


3.3  Spring AOP拦截器调用的实现


3.3.1  JdkDynamicAopProxy的invoke拦截
上面我们看到了在Spring中通过ProxyFactoryBean实现AOP功能的第一步,得到AopProxy代理对象的基本过程,以及通过使用JDK和CGLIB最终产生AopProxy代理对象的实现原理。下面我们看看AopProxy代理对象的拦截机制是怎样发挥作用和实现AOP功能的。在JdkDynamicAopProxy中生成Proxy对象时,我们回顾一下它的AopProxy代理对象的生成调用,如下所示。

Java代码  收藏代码
  1. Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);  

 

这里的this参数对应的是InvocationHandler对象,InvocationHandler是JDK定义的反射类的一个接口,这个接口定义了invoke方法,而这个invoke方法是作为JDKProxy代理对象进行拦截的回调入口出现的。我们看到,在JdkDynamicAopProxy实现了InvocationHandler接口,也就是说当Proxy对象的代理方法被调用时,JdkDynamicAopProxy的invoke方法作为Proxy对象的回调函数而被触发,从而通过invoke的具体实现,来完成对目标对象方法调用的拦截或者说功能增强的工作。下面我们看看JdkDynamicAopProxy的invoke方法实现,如代码清单3-18所示。可以看到,对Proxy对象完成的代理设置是在invoke方法中完成的,这些设置包括获取目标对象、拦截器链,同时把这些对象作为输入,创建了ReflectiveMethodInvocation对象,通过这个ReflectiveMethodInvocation对象来完成对AOP功能实现的封装。在这个invoke方法中,包含了一个完整的拦截器链对目标对象的拦截过程,比如获得拦截器链并对其中的拦截器进行配置,逐个运行拦截器链里的拦截增强,直到最后对目标对象方法的运行,等等。


代码清单3-18  AopProxy代理对象的回调

 

Java代码  收藏代码
  1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  2.  MethodInvocation invocation = null;  
  3.  Object oldProxy = null;  
  4.  boolean setProxyContext = false;  
  5.  TargetSource targetSource = this.advised.targetSource;  
  6.  Class targetClass = null;  
  7.  Object target = null;  
  8.  try {  
  9.   if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {  
  10.    // The target does not implement the equals(Object) method itself.  
  11.    return equals(args[0]);  
  12.   }  
  13.   if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {  
  14.    // The target does not implement the hashCode() method itself.  
  15.    return hashCode();  
  16.   }  
  17.   if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&  
  18.     method.getDeclaringClass().isAssignableFrom(Advised.class)) {  
  19.    // Service invocations on ProxyConfig with the proxy config...  
  20.    return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);  
  21.   }  
  22.   Object retVal = null;  
  23.   if (this.advised.exposeProxy) {  
  24.    // Make invocation available if necessary.  
  25.    oldProxy = AopContext.setCurrentProxy(proxy);  
  26.    setProxyContext = true;  
  27.   }  
  28.   /** 
  29.   * May be null. Get as late as possible to minimize the time we "own" the 
  30.   * target, in case it comes from a pool. 
  31.   */  
  32.   //得到目标对象的地方。  
  33.   target = targetSource.getTarget();  
  34.   if (target != null) {  
  35.    targetClass = target.getClass();  
  36.   }  
  37.   // Get the interception chain for this method.  
  38.   // 这里获得定义好的拦截器链。  
  39.   List<Object> chain = this.advised.getInterceptorsAndDynamicInterception  
  40. Advice(method, targetClass);  
  41.   /** 
  42.   * Check whether we have any advice. If we don't, we can fallback on 
  43.   * direct reflective invocation of the target, and avoid creating a MethodInvocation. 
  44.   */  
  45.   
  46.   // 如果没有设定拦截器,那么我们就直接调用target的对应方法。  
  47.   if (chain.isEmpty()) {  
  48.    /** 
  49.    * We can skip creating a MethodInvocation: just invoke the target directly 
  50.    * Note that the final invoker must be an InvokerInterceptor so we 
  51.    * know it does nothing but a reflective operation on the target, and no hot 
  52.   * swapping or fancy proxying. 
  53.    */  
  54.    retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);  
  55.   }  
  56.   else {  
  57.    // We need to create a method invocation...  
  58.    /** 
  59.    * 如果有拦截器的设定,那么需要调用拦截器之后才调用目标对象的相应方法, 
  60.    * 通过构造一个ReflectiveMethodInvocation来实现,下面我们会看 
  61.    * 这个ReflectiveMethodInvocation类的具体实现。 
  62.    */  
  63.    invocation = new ReflectiveMethodInvocation(proxy, target, method,  
  64.   args, targetClass, chain);  
  65.    // Proceed to the joinpoint through the interceptor chain.  
  66.    //沿着拦截器链继续前进。  
  67.    retVal = invocation.proceed();  
  68.   }  
  69.   
  70.   // Massage return value if necessary.  
  71.   if (retVal != null && retVal == target && method.getReturnType().  
  72.  isInstance(Proxy) && !RawTargetAccess.class.isAssignableFrom  
  73.     (method.getDeclaringClass())) {  
  74.    /** 
  75.    * Special case: it returned "this" and the return type of the method 
  76.    * is type-compatible. Note that we can't help if the target sets 
  77.    * a reference to itself in another returned object. 
  78.    */  
  79.    retVal = Proxy;  
  80.   }  
  81.   return retVal;  
  82.  }  
  83.  finally {  
  84.   if (target != null && !targetSource.isStatic()) {  
  85.    // Must have come from TargetSource.  
  86.    targetSource.releaseTarget(target);  
  87.   }  
  88.   if (setProxyContext) {  
  89.    // Restore old proxy.  
  90.    AopContext.setCurrentProxy(oldProxy);  
  91.   }  
  92.  }  
  93. }  

 

3.3.2  Cglib2AopProxy的intercept拦截
在分析Cglib2AopProxy的AopProxy代理对象生成的时候,我们知道对于AOP的拦截调用,其回调是在DynamicAdvisedInterceptor对象中实现的,这个回调的实现在intercept方法中,如代码清单3-19所示。Cglib2AopProxy的intercept回调方法的实现和JdkDynamicAopProxy的回调实现是非常类似的,只是在Cglib2AopProxy中构造的是CglibMethodInvocation对象来完成拦截器链的调用,而在JdkDynamicAopProxy中是通过构造ReflectiveMethodInvocation对象来完成这个功能的。


代码清单3-19  DynamicAdvisedInterceptor的intercept

Java代码  收藏代码
  1. public Object intercept(Object proxy, Method method, Object[] args, MethodProxy  
  2. methodProxy) throws Throwable {  
  3.  Object oldProxy = null;  
  4.  boolean setProxyContext = false;  
  5.  Class targetClass = null;  
  6.  Object target = null;  
  7.  try {  
  8.   if (this.advised.exposeProxy) {  
  9.    // Make invocation available if necessary.  
  10.    oldProxy = AopContext.setCurrentProxy(proxy);  
  11.    setProxyContext = true;  
  12.   }  
  13.   /** 
  14.   * May be <code>null</code>. Get as late as possible to minimize the time we 
  15.   * "own" the target, in case it comes from a pool. 
  16.   */  
  17.   target = getTarget();  
  18.   if (target != null) {  
  19.    targetClass = target.getClass();  
  20.   }  
  21.   //从advised中取得配置好的AOP通知。  
  22.   List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice  
  23.  (method, targetClass);  
  24.   Object retVal = null;  
  25.   /** 
  26.   * Check whether we only have one InvokerInterceptor: that is, 
  27.   * no real advice, but just reflective invocation of the target. 
  28.   */  
  29.   // 如果没有AOP通知配置,那么直接调用target对象的调用方法。  
  30.   if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {  
  31.    /** 
  32.    * We can skip creating a MethodInvocation: just invoke the target directly. 
  33.    * Note that the final invoker must be an InvokerInterceptor, so we know 
  34.    * it does nothing but a reflective operation on the target, and no hot 
  35.    * swapping or fancy proxying. 
  36.    */  
  37.    retVal = methodProxy.invoke(target, args);  
  38.   }  
  39.   else {  
  40.    //通过CglibMethodInvocation来启动advice通知。  
  41.    retVal = new CglibMethodInvocation(proxy, target, method, args,  
  42.   targetClass, chain, methodProxy).proceed();  
  43.   }  
  44.   retVal = massageReturnTypeIfNecessary(proxy, target, method, retVal);  
  45.   return retVal;  
  46.  }  
  47.  finally {  
  48.   if (target != null) {  
  49.    releaseTarget(target);  
  50.   }  
  51.   if (setProxyContext) {  
  52.    // Restore old proxy.  
  53.    AopContext.setCurrentProxy(oldProxy);  
  54.   }  
  55.  }  
  56. }  

 

3.3.3  目标对象方法的调用
如果没有设置拦截器,那么会对目标对象的方法直接进行调用,对于JdkDynamicAopProxy代理对象,这个对目标对象的方法调用是通过AopUtils使用反射机制完成的,在AopUtils.
invokeJoinpointUsingReflection的方法中实现,如代码清单3-20所示。在这个调用中,首先得到调用方法的反射对象,然后使用invoke启动对方法反射对象的调用。

代码清单3-20  使用反射完成目标对象的方法调用

Java代码  收藏代码
  1. public static Object invokeJoinpointUsingReflection(Object target, Method method,  
  2. Object[] args)  
  3.  throws Throwable {  
  4.  // Use reflection to invoke the method.  
  5.  // 这里是使用反射调用target对象方法的地方。  
  6.  try {  
  7.   ReflectionUtils.makeAccessible(method);  
  8.   return method.invoke(target, args);  
  9.  }  
  10.  catch (InvocationTargetException ex) {  
  11.   // Invoked method threw a checked exception.  
  12.   // We must rethrow it. The client won't see the interceptor.  
  13.   throw ex.getTargetException();  
  14.  }  
  15.  catch (IllegalArgumentException ex) {  
  16.   throw new AopInvocationException("AOP configuration seems to be invalid:  
  17.  tried calling method [" + method + "] on target [" + target + "]", ex);  
  18.  }  
  19.  catch (IllegalAccessException ex) {  
  20.   throw new AopInvocationException("Could not access method [" + method +  
  21.  "]", ex);  
  22.  }  
  23. }  

 

对于使用Cglib2AopProxy的代理对象,它对目标对象的调用是通过CGLIB的MethodProxy对象来直接完成的,这个对象的使用是由CGLIB的设计来决定的;对于具体的调用在DynamicAdvisedInterceptor的intercept方法中可以看到,使用的是CGLIB封装好的功能,相对JdkDynamicAopProxy的实现来说,形式上看起来较为简单,但它们的功能却都是一样的,都是完成对目标对象方法的调用,具体的代码实现如下。

Java代码  收藏代码
  1. retVal = methodProxy.invoke(target, args);  
原文地址:https://www.cnblogs.com/badboyf/p/6044584.html