今天我们一起来看一下在Spring2.0中完成AOP功能的核心类ProxyFactoryBean。先了解一下ProxyFactoryBean的继承结构,如下图所示:
ProxyFactoryBean对外提供的主要服务都定义在了接口Advised中,因此我们在此可以暂且忽略其余接口的内容,因为这些接口提供的服务大都是被Spring的bean容器用到,而不是我们这里的关注点。而ProxyConfig是一个配置类,里面提供了访问/设置ProxyFactoryBean某些属性的方法,这些我们也留待以后探讨。
追根溯源,我们就来研究一下Advised究竟为ProxyFactoryBean定义了哪些服务,Advised接口如下图所示:
看到此处,我们就能大致对上篇文章中所列举的配置文件中AOP相关的三部分配置的内容是如何工作的有一个了解。插几句题外话,对于任何一套系统,都可以先从系统中的接口开始摸索,因为接口代表着标准,代表着协议,代表着某函数,模块甚至系统对外提供的服务。熟悉了接口,就能对系统的功用有个大致的了解,然后如果你有兴趣,可以再去深究系统究竟是如何实现接口提供的服务。
言归正传,可以看出该接口主要提供了两方面的服务,一是设置/获取被代理(编织)的对象;二是设置/删除拦截器。为了与AOP配置文件相对应,我们再来回顾一下上篇文章中提到的配置文件:
<beans>
<bean id="kwikEMartTarget" class="demo.ApuKwikEMart">
</bean>
<!-- 方法调用前通知 -->
<bean id="welcomeAdvice" class="demo.advice.WelcomeAdvice" />
<!-- 方法调用后通知 -->
<bean id="thankYouAdvice" class="demo.advice.ThankYouAdvice" />
<!-- 环绕调用通知 -->
<bean id="onePerCustomerInterceptor" class="demo.advice.OnePerCustomerInterceptor" />
<!-- 异常调用通知 -->
<bean id="kwikEmartExceptionAdvice" class="demo.advice.KwikEmartExceptionAdvice" />
<bean id="kwikEMart" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="demo.KwikEMart" />
<property name="interceptorNames">
<list>
<value>welcomeAdvice</value>
<value>thankYouAdvice</value>
<value>onePerCustomerInterceptor</value>
<value>kwikEmartExceptionAdvice</value>
</list>
</property>
<property name="target">
<ref bean="kwikEMartTarget" />
</property>
</bean>
</beans>
上述配置文件中我们为类ProxyFactoryBean的属性target配置了kwikEMartTarget,实际上在Spring内部就是调用了ProxyFactoryBean实现的Advised接口中的方法SetTargetSource,从而将被代理的类设置到了ProxyFactoryBean中等待编织。
为ProxyFactoryBean配置的另一个属性是interceptorNames,这个属性是一个列表类型,其中配置了ProxyFactoryBean用来编织进被代理对象kwikEMartTarget的所有拦截器。上例配置文件中为被代理对象kwikEMartTarget配置了四种类型的拦截器,分别是前置拦截器,后置拦截器,环绕拦截器,和异常拦截器,分别在kwikEMartTarget的方法执行前,后,前/后和抛出异常的时执行定义在拦截器中的方法。
配置文件还为ProxyFactoryBean配置了一个名叫proxyInterfaces的属性,这个稍后再来研究。
理解了配置文件,明白了ProxyFactoryBean的大致实现方式,接下来我们再从一个使用者的角度来追踪AOP的具体实现。
如上图所示,ProxyFactoryBean暴露给外部调用的接口是getObject(),我们就从这里作为切入点来研究。该方法返回一个经过编织的被代理对象的代理,有点拗口,呵呵。该代理有两种模式,一种是单体模式,另一种是原型模式。究竟使用哪种模式取决于配置文件中为ProxyFactoryBean的singleton属性配置的值,该值对应于如上类图中ProxyFactoryBean的singleton属性,该属性不仅仅决定ProxyFactoryBean所返回代理的模式,在具体的实现细节中还会用到,这个稍后再探讨。
如果使用单体模式获得代理对象,方法getObject将调用类图中的方法getSingletonInstance以返回一个单体代理,该方法最终返回的是ProxyFactoryBean中名为singletonInstance的私有变量,当然在返回前ProxyFactoryBean已经对其进行了初始化;反之如果要返回原型模式的代理对象,方法getObject则会调用方法newPrototypeInstance来返回一个代理的原型对象。
不论采用哪种模式返回代理对象,最终是通过一个名为createAopProxy的方法返回一个AopProxy,该方法定义在类AdvisedSupport中,如上类图所示,再通过该AopProxy获得最终我们想要的代理对象,搞定,收工。。。
稍等,大致明白了AOP是怎么一回事,突然有一个问题冒了出来,Spring的AOP实现源码中大量使用了Proxy这个词,自此理解我们认为Spring使用的是设计模式中的Proxy Pattern(代理模式)。但仔细思考一下整个的实现方式,这似乎和Decorator Pattern(装饰模式)的要义更加符合。那我们首先看一下Head First中对于Decorator Pattern的描述以及类图:
The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
从上可以看出装饰模式的主要作用就是为了给被装饰类添加新的功能,从而符合OO原则中的
Classes should be open for extension, but closed for modification.
将上述装饰模式的模型和我们讨论的AOP实现方式进行比对我们就会发现,在AOP配置文件中ProxyFactoryBean有可能被我们忽略的一个属性proxyInterfaces,该属性对应到上述类图中,便是IComponent接口,该接口是被装饰类和装饰类共同实现的接口,目的是为了类型匹配,或者更直白的说,就是为了能将装饰类直接当做被装饰类使用,而不用或少量修改其余的代码即可。此外也避免了直接只用继承来扩展功能所带来的生成大量子类的问题。装饰模式给我们带来了解决问题的新思路,如果你想扩展基类的功能,不一定要使用继承(inheritance),有时候使用复合(composition)和代理(delegation)能产生更好的设计。
既然这看起来更像是装饰模式,那为什么又称作AopProxy呢。先别急,下次我们来共同探讨一下更细节内容,关于AopProxy是如何产生的,以及其他一些问题,我们就能判断这究竟是代理模式还是装饰模式了。
To be continued…