《Spring技术内幕:深入解析Spring架构与设计原理》笔记之三(Spring的AOP的实现)

什么是AOP?

AOP:Aspect-Oriented Programming 面向方面编程或面向切面

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

AOP技术:

 

  • AspectJ:源代码和字节码级别的编织器,用户需要使用不同于Java的新语言。
  • AspectWerkz:AOP框架,使用字节码动态编码器和XML配置。
  • JBoss-AOP:基于拦截器和元数据的AOP框架,运行在Jboss应用服务器上。以及在AOP中用到的一些相关的技术实现。
  • BCEL(Byte-Code Engineering Library):Java字节码操作类库。
  • Javassist:Java字节码操作类库,JBoss的一个子项目

AOP联盟定义的AOP体系结构

 

“基础”(base)可以视为待增强对象或者说目标对象;

“切面”(aspect)通常包含对于基础的增强应用;

“配置”(configuration)可以看成是一种编织,通过在AOP体系中提供这个配置环境,可以把基础和切面结合起来,从而完成面对目标对象的编织实现。

在Spring AOP实现中,使用Java语言来实现增强对象与切面增强应用,并为这两者的结合提供了配置环境。对于编织配置,毫无疑问,可以使用IOC容器来完成;对于POJO对象的配置,本来就是Spring的核心IOC容器的强项。因此,对于Spring的AOP开发而言,使用POJO就能完成AOP任务。

对Spring平台或者说生态系统来说,AOP是Spring框架的核心功能模块之一。AOP和IOC容器的结合使用,为应用开发或Spring自身功能的扩展都是提供了许多便利。Spring AOP的实现和其他特性的实现一样,除了可以使用Spring本身提供的AOP实现之外,还封装了业界优秀的AOP解决方案AspectJ来供应用使用。

Advice通知

advice(通知)定义在连接点做什么,为切面增强提供织入接口。在Spring AOP中,它主要描述Spring AOP围绕方法调用而注入的切面行为。Advice是AOP联盟定义的一个接口,具体的接口定义在org.aopalliance.aop.Advice中。在Spring AOP的实现中,使用了这个统一接口,并通过这个接口,为AOP切面增强的织入功能做了更多的细化和扩展,比如提供了更具体的通知类型,如BeforeAdvice、AfterAdvice、ThrowsAdvice等。作为Spring AOP定义的接口类,具体的切面增强可以通过这些接口集成到AOP框架中发挥作用。

从BeforeAdvice开始:

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

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

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

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

Pointcut切点

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

切点在Spring AOP中的类继承体系

在Pointcut的基本接口定义中可以看到,需要返回一个MethodMatcher。对于Point的匹配判断功能,具体是由这个返回的MethodMatcher来完成的,也就是说,由这个MethodMatcher来判断是否需要对当前方法调用进行增强,或者是否需要对当前调用方法应用配置好的Advice通知。在Pointcut的类继承关系中,以正则表达式切点JdkRegexpMethodPointcut类完成通过正则表达式对方法名进行匹配的功能。在JdkRegexpMethodPointcut的基类StaticMethodMatcherPointcut的实现中可以看到,设置MethodMatcher为StaticMethodMatcher,同时JdkRegexpMethodPointcut也是这个MethodMatcher的子类。

可以看到,在Pointcut中,通过这样的类继承关系,MethodMatcher对象实际上是可以被配置成JdkRegexpMethodPointcut来完成方法的匹配判断的。在JdkRegexpMethodPointcut中,可以看到一个matches方法,这个matches方法是MethodMatcher定义的接口方法。在JdkRegexpMethodPointcut的实现中,这个matches方法就是使用正则表达式来对方法名进行匹配的地方。关于在AOP框架中对matches方法的调用,会在下面的Spring AOP实现中介绍,这里只是先简单提一下。要了解matches在AOP框架中的调用位置,比较简单的方法就是以matches方法作为起始点,对它的方法调用关系进行追溯。

在对matches方法的调用关系中可以看到,是在JdkDynamicAopProxy的invoke方法中触发了对matches方法的调用。invoke回调的实现是使用JDK动态代理完成AOP功能的一部分,关于这部分的实现原理,在下面AOP的实现分析中有详细的阐述,这里就不进行太多的说明了。

在JdkRegexpMethodPointcut中,通过JDK来实现正则表达式的匹配。

在Spring AOP中,还提供其他的MethodPointcut,比如通过方法名匹配进行Advice匹配的NameMatchMethodPointcut。

Advisor通知器

完成对目标方法的切面增强设计(Advice)和关注点的设计(Pointcut)以后,需要一个对象把它们结合起来,完成这个作用的就是Advisor(通知器)。通过Advisor,可以定义应该使用哪个通知并在哪个关注点使用它,也就是说通过Advisor,把Advice和Pointcut结合起来,这个结合为使用IOC来配置AOP应用,或者说即开即用地使用AOP基础设施,提供了便利。在Spring AOP中,我们以一个Advisor的实现(DefaultPointcutAdvisor)为例,来了解Advisor的工作原理。在DefaultPointcutAdvisor中,有两个属性,分别是advice和pointcut。通过这两个属性,可以配置Advice和Pointcut。

Spring AOP的设计与实现

JVM的动态代理特性

在spring aop实现中,使用的核心技术是动态代理,而这种动态代理实际上是JDK的一个特性。通过JDK的动态代理特性,可以为任意Java对象创建代理对象,对于具体使用来说,这个特性是通过Java Reflection API来完成的。

Spring AOP的设计分析

以动态代理技术为基础,设计出了一系列AOP的横切实现,比如前置通知,返回通知、异常通知等。同时,Spring AOP的内部设计可以看到,为了让AOP起作用,需要完成一系列过程,比如,需要为目标对象建立代理对象,这个代理对象可以通过使用JDK的Proxy来完成,也可以通过第三方的类生成器CGLIB来完成。然后,还需要启动代理对象的拦截器来完成各种横切面的织入,这一系列的织入设计是通过一系列Adapter来实现。通过一系列Adapter的设计,可以把AOP的横切面设计和Proxy模式有机地结合起来,从而实现在AOP中定义好的各种织入方式。

Spring AOP的应用场景

Spring AOP为IOC的使用提供更多便利,一方面,应用可以直接使用AOP的功能,设计应用的横切关注点,把跨越应用程序多个模块的功能抽象出来,并通过简单的AOP的使用,灵活地编制到模块中,比如可以通过AOP实现应用程序中的日志功能。另一方面,在Spring内部,一些支持模块也通过Spring AOP来实现的,比如后面将要详细介绍的事务处理。从两个角度就可以看到Spring AOP的核心地位了。

建立AOPProxy代理对象

设计原理

在spring的AOP模块中,一个主要的部分是代理对象的生成,而对于Spring应用,可以看到,是通过配置和调用Spring的ProxyFactoryBean来完成这个任务的。在ProxyFactoryBean中,封装了主要代理对象的生成过程。在这个生成过程中,可以使用JDK的Proxy和GCLIB两种生成方式。

类继承关系

在这个类继承关系中,可以看到完成AOP应用的类,比如AspectJProxyFactory、ProxyFactory和ProxyFactoryBean,它们都在同一个类的继承体系下,都是ProxyConfig、AdvisedSupport和ProxyCreatorSupport的子类。作为共同基类,可以将ProxyConfig看成是一个数据基类,这个数据基类为ProxyFactoryBean这样的子类提供了配置属性;在另一个基类AdvisedSupport的实现中,封装了AOP对通知和通知器的相关操作,这些操作对于不同的AOP的代理对象的生成都是一样的,但对于具体的AOP代理对象的创建,AdvisedSupport把它交给它的子类们去完成;对于ProxyCreatorSupport,可以将它看成是其子类创建AOP代理对象的生成,根据不同的需要分别由ProxyFactoryBean、AspectJProxyFactory和ProxyFactory来完成。对于需要使用AspectJ的AOP应用,AspectJProxyFactory起到集成Spring和AspectJ的作用;对于使用Spring AOP的应用,ProxyFactoryBean和ProxyFactory都提供了AOP功能的封装,只是使用ProxyFactoryBean,可以在IOC容器中完成声明式配置,而使用ProxyFactory,则需要编程式地使用Spring  AOP的功能。

配置ProxyFactoryBean

1)定义使用的通知器Advisor,这个通知器应该作为一个Bean来定义。很重要的一点是,这个通知器的实现定义了需要对目标对象进行增强的切面行为,也就是Advice通知。

2)定义ProxyFactoryBean,把它作为另一个Bean来定义,它是封装AOP功能的主要类。在配置ProxyFactoryBean时,需要设定与AOP实现相关的重要属性,比如proxyInterface、interceptorNames和target等。从属性名称可以看出,interceptorNames属性的值往往设置为需要定义的通知器,因为这些通知器在ProxyFactoryBean的AOP配置下,是通过使用代理对象的拦截器机制起作用的。

3)定义target属性,作为target属性注入的Bean,是需要用AOP通知器中的切面应用来增强的对象,也就是前面提到的base对象。

ProxyFactoryBean生成AOPProxy代理对象

在ProxyFactoryBean中,通过interceptorNames属性来配置通知器的地方。在ProxyFactoryBean中,需要为target目标对象生成Proxy代理对象,从而为AOP横切面的编织做好工作。

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

AOPProxy的生成过程

生成Singleton的代理对象在getSingletonInstance()的代码中完成,这个方法是ProxyFactoryBean生成AOpProxy代理对象的调用入口。代理对象会封装对target目标对象的调用,也就是说针对target对象的方法调用行为会被这里生成的代理对象所拦截。具体的生成过程是,首先读取ProxyFactoryBean中的配置,为生成代理对象做好必要的准备,比如设置代理的方法调用接口等。Spring通过AOPProxy类来具体生成代理对象。

AOPProxy是一个接口,它由两个子类实现,一个是Cglib2AOPProxy,另一个是JdkDynamicProxy。顾名思义,对这两个AopProxy接口的子类的实现,Spring分别通过CGLIB和JDK来生成需要的Proxy代理对象

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

JDK生成AopProxy代理对象

生成代理对象时,需要指明三个参数,一是类装载器、一个是代理接口、另外一个就是Proxy回调方法所在的对象,这个对象需要实现InvocationHandler接口。

CGLIB生成AopProxy代理对象

使用的是设置好的callback回调,这是由GCLIB的使用来决定的。

Spring  AOP拦截器的实现

设计原理

通过JDK或者GCLIB的方式生成代理对象,相关的拦截器已经配置到代理对象中去了,拦截器在代理对象中起作用是通过对这些方法的回调来完成的。

如果使用JDK的Proxy来生成代理对象,那么需要通过InvocationHandler来设置拦截器回调;而如果使用CGLIB来生成代理对象,就需要根据CGLIB的使用要求,通过DynamicAdvisedInterceptor来完成回调。

jdkDynamicAOPProxy的invoke拦截

在Spring中通过ProxyFactoryBean实现AOP功能的第一步,得到AopProxy代理对象的基本过程,以及通过使用JDK和CGLIB最终产生AopProxy代理对象的实现原理。

Cglib2AOPProxy的intercept拦截

目标对象方法的调用

对于jdkDynamicAopProxy代理对象,这个目标对象的方法调用是通过AopUtils使用反射机制在AopUtils.invokeJoinpointUsingRefelection方法中实现的。在这个调用中,首先得到方法的反射方法,然后使用invoke启动对方法反射对象的调用。

Aop拦截器链的调用

对拦截器链的调用都是在ReflectionMethodInvocation中通过proceed方法的实现。在proceed方法中,会逐个运行拦截器的拦截方法。

配置通知器

这个interceptorOrInterceptionAdvice是获得的拦截器,它通过拦截器机制对目标对象的行为增强起作用。为了提高取得拦截器链的效率,还为这个拦截器链设置了缓存。

在ProxyFactoryBean的getObject方法中对advisor进行初始化时,从XML配置中获取了advisor通知器。

Actice通知的实现

一是调用Adapter的support方法,通过这个方法来判断取得的advice属于什么类型的advice通知,从而根据不同的advice类型来注册不同的AdviceInterceptor,也就是前面看到的那些拦截器;

二是这些AdviceInterceptor都是Spring AOP框架设计好了,是为了实现不同的advice功能提供服务。

ProxyFactory实现AOP

由ProxyFactory的getProxy方法取得AopProxy代理对象,getProxy方法的实现使用ProxyFactory的基类ProxyCreatorSupport的createProxy方法来生成AopProxy代理对象,而AopProxy代理对象的生成是由AopProxyFactory来完成的,会生成JDK或者CGLIB的代理对象。

Spring Aop的高级特性

Spring提供许多现成的TargetSource实现,比如HotSwappableTargetSource,HotSwappableTargetSource使用户可以以线程安全的方式切换目标对象,提供所谓的热交换功能。这个特性是很有用的,尽管它的开启需要AOP应用进行显示的配置,但配置不复杂,在使用时,只需要把HotSwappableTargetSource配置到ProxyFactoryBean的target属性就可以了,在需要真正的目标对象时,调用HotSwappableTargetSource的swap方法就可以完成。由此可见,对HotSwappableTargetSource的热交换功能的使用,是需要触发swap方法调用的。这个swap方法的实现很简单,它完成target对象的替换,也就是说,它使用新的target对象来替换原有的target对象。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值