技术开发 频道

Spring AOP: Spring之面向方面编程

Spring中的advisor
在Spring中,一个advisor就是一个aspect的完整的模块化表示。 一般地,一个advisor包括通知和切入点。

撇开导入这种特殊情况,任何advisor可被用于任何通知。 org.springframework.aop.support.DefaultPointcutAdvisor 是最通用的advisor类。例如,它可以和MethodInterceptor、 BeforeAdvice或者ThrowsAdvice一起使 用。

Spring中可以将advisor和通知混合在一个AOP代理中。例如,你可以在一个代理配置中 使用一个对around通知、throws通知和before通知的拦截:Spring将自动创建必要的拦截器链。

用ProxyFactoryBean创建AOP代理
如果你在为你的业务对象使用Spring的IoC容器(例如ApplicationContext或者BeanFactory), 你应该会或者你愿意会使用Spring的aop FactoryBean(记住,factory bean引入了一个间接层, 它能创建不同类型的对象).

在spring中创建AOP proxy的基本途径是使用org.springframework.aop.framework.ProxyFactoryBean. 这样可以对pointcut和advice作精确控制。但是如果你不需要这种控制,那些简单的选择可能更适合你。 

基本概要
ProxyFactoryBean,和其他Spring的 FactoryBean实现一样,引入一个间接的层次。如果你 定义一个名字为foo的ProxyFactoryBean, 引用foo的对象所看到的不是ProxyFactoryBean 实例本身,而是由实现ProxyFactoryBean的类的 getObject()方法所创建的对象。这个方法将创建一个包装了目标对象 的AOP代理。

使用ProxyFactoryBean或者其他IoC可知的类来创建AOP代理 的最重要的优点之一是IoC可以管理通知和切入点。这是一个非常的强大的功能,能够实 现其他AOP框架很难实现的特定的方法。例如,一个通知本身可以引用应用对象(除了目标对象, 它在任何AOP框架中都可以引用应用对象),这完全得益于依赖注入所提供的可插入性。

JavaBean的属性
类似于Spring提供的绝大部分FactoryBean实现一样, ProxyFactoryBean也是一个javabean,我们可以利用它的属性来:

指定你将要代理的目标

指定是否使用CGLIB

一些关键属性来自org.springframework.aop.framework.ProxyConfig :它是所有AOP代理工厂的父类。这些关键属性包括:

proxyTargetClass: 如果我们应该代理目标类, 而不是接口,这个属性的值为true。如果这是true,我们需要使用CGLIB。

optimize: 是否使用强优化来创建代理。不要使用 这个设置,除非你了解相关的AOP代理是如何处理优化的。目前这只对CGLIB代理有效;对JDK 动态代理无效(默认)。

frozen: 是否禁止通知的改变,一旦代理工厂已经配置。 默认是false。

exposeProxy: 当前代理是否要暴露在ThreadLocal中, 以便它可以被目标对象访问。(它可以通过MethodInvocation得到,不需要ThreadLocal)。 如果一个目标需要获得它的代理并且exposeProxy的值是ture,可以使用 AopContext.currentProxy()方法。

aopProxyFactory: 所使用的AopProxyFactory具体实现。 这个参数提供了一条途径来定义是否使用动态代理、CGLIB还是其他代理策略。默认实现将适当地选择动态 代理或CGLIB。一般不需要使用这个属性;它的意图是允许Spring 1.1使用另外新的代理类型。

其他ProxyFactoryBean特定的属性包括:

proxyInterfaces: 接口名称的字符串数组。如果这个 没有提供,CGLIB代理将被用于目标类。

interceptorNames: Advisor、interceptor或其他 被应用的通知名称的字符串数组。顺序是很重要的。这里的名称是当前工厂中bean的名称,包 括来自祖先工厂的bean的名称。

singleton: 工厂是否返回一个单独的对象,无论 getObject()被调用多少次。许多FactoryBean 的实现提供这个方法。默认值是true。如果你想要使用有状态的通知--例如,用于有状态的 mixin--将这个值设为false,使用prototype通知。

代理接口
让我们来看一个简单的ProxyFactoryBean的实际例子。这个例子涉及到 :

一个将被代理的目标bean,在这个例子里,这个bean的被定义为"personTarget".

一个advisor和一个interceptor来提供advice.

一个AOP代理bean定义,该bean指定目标对象(这里是personTarget bean), 代理接口,和使用的advice.

<bean id="personTarget" class="com.mycompany.PersonImpl"> <property name="name"><value>Tony</value></property> <property name="age"><value>51</value></property> </bean> <bean id="myAdvisor" class="com.mycompany.MyAdvisor"> <property name="someProperty"><value>Custom string property value</value></property> </bean> <bean id="debugInterceptor" class="org.springframework.aop.interceptor.NopInterceptor"> </bean> <bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"><value>com.mycompany.Person</value></property> <property name="target"><ref local="personTarget"/></property> <property name="interceptorNames"> <list> <value>myAdvisor</value> <value>debugInterceptor</value> </list> </property> </bean>

请注意:person bean的interceptorNames属性提供一个String列表, 列出的是该ProxyFactoryBean使用的,在当前bean工厂定义的interceptor或者advisor的 名字(advisor,interceptor,before,after returning,和throws advice 对象皆可)。 Advisor在该列表中的次序很重要。

你也许会对该列表为什么不采用bean的引用存有疑问。 原因就在于如果ProxyFactoryBean的singleton属性被设置为false, 那么bean工厂必须能返回多个独立的代理实例。 如果有任何一个advisor本身是prototype的,那么它就需要返回独立的实例, 也就是有必要从bean工厂获取advisor的不同实例,bean的引用在这里显然是不够的。
上面定义的“person”bean定义可以作为Person接口的实现来使用,如下所示:

Person person = (Person) factory.getBean("person");
在同一个IoC的上下文中,其他的bean可以依赖于Person接口,就象依赖于一个普通的java对象一样。

<bean id="personUser" class="com.mycompany.PersonUser"> <property name="person"><ref local="person" /></property> </bean>

 在这个例子里,PersonUser类暴露了一个类型为Person的属性。 只要是在用到该属性的地方,AOP代理都能透明的替代一个真实的Person实现。 但是,这个类可能是一个动态代理类。也就是有可能把它类型转换为一个Advised接口 (该接口在下面的章节中论述) 。

代理类
如果你需要代理的是类,而不是一个或多个接口,又该怎么办呢?

想象一下我们上面的例子,如果没有Person接口, 我们需要通知一个叫Person的类, 而且该类没有实现任何业务接口。在这种情况下,你可以配置Spring使用CGLIB代理, 而不是动态代理。你只要在上面的ProxyFactoryBean定义中把 它的proxyTargetClass属性改成true就行了。

只要你愿意,即使在有接口的情况下,你也可以强迫Spring使用CGLIB代理。

CGLIB代理是通过在运行期产生目标类的子类来进行工作的。 Spring可以配置这个生成的子类,来代理原始目标类的方法调用。这个子类是用 Decorator设计模式置入到advice中的。

CGLIB代理对于用户来说应该是透明的。然而,还有以下一些因素需要考虑:

Final方法不能被通知,因为不能被重写。

你需要在你的classpath中包括CGLIB的二进制代码,而动态代理对任何JDK都是可用的.

CGLIB和动态代理在性能上有微小的区别,对Spring 1.0来说,后者稍快。 另外,以后可能会有变化。在这种情况下性能不是决定性因素

0
相关文章