技术开发 频道

Spring AOP: Spring之面向方面编程

Spring提供了TargetSource的概念,由 org.springframework.aop.TargetSource接口定义。这个接口负责返回实现切入点的 “目标对象”。每次AOP代理处理方法调用时,目标实例都会用到TargetSource实现。

使用Spring AOP的开发者一般不需要直接使用TargetSources,但是这提供了一种强大的方法来支持池,热交换, 和其它复杂目标。例如,一个支持池的TargetSource可以在每次调用时返回不同的目标对象实例,使用池来管理实例。

如果你没有指定TargetSource,就使用缺省的实现,它封装了一个本地对象。每次调用会返回相同的目标对象 (和你期望的一样)。

让我们来看一下Spring提供的标准目标源,以及如何使用它们。

当使用定制目标源时,你的目标通常需要定义为prototype bean,而不是singleton bean。这使得 Spring在需要的时候创建一个新的目标实例。
可热交换的目标源
org.springframework.aop.target.HotSwappableTargetSource 允许切换一个AOP代理的目标,而调用者维持对它的引用。

修改目标源的目标会立即起作用。并且HotSwappableTargetSource是线程安全的。

你可以通过HotSwappableTargetSource的swap()方法 来改变目标,就象下面一样:

HotSwappableTargetSource swapper =
(HotSwappableTargetSource) beanFactory.getBean("swapper");
Object oldTarget = swapper.swap(newTarget);
所需的XML定义如下:

<bean id="initialTarget" class="mycompany.OldTarget"> </bean> <bean id="swapper" class="org.springframework.aop.target.HotSwappableTargetSource"> <constructor-arg><ref local="initialTarget"/></constructor-arg> </bean> <bean id="swappable" class="org.springframework.aop.framework.ProxyFactoryBean" > <property name="targetSource"> <ref local="swapper"/> </property> </bean>

上面的swap()调用会修改swappable这个bean的目标。持有对这个bean应用的客户端 将不会知道这个变化,但会立刻转为使用新的目标对象。

虽然这个例子没有添加任何通知--使用TargetSource也没必要添加通知-- 当然任何TargetSource都可以和任何一种通知一起使用。

支持池的目标源
使用支持池的目标源提供了一种和无状态的session EJB类似的编程模式,在无状态的session EJB中,维护了 一个相同实例的池,提供从池中获取可用对象的方法。

Spring的池和SLSB的池之间的重要区别在于Spring的池可以被应用到任何普通Java对象。就象Spring的通用 的做法,这个业务也可以以非侵入的方式被应用。

Spring直接支持Jakarta Commons Pool 1.1,它是一种非常高效的池实现。使用这个功能,你需要在你的应用的 classpath中添加commons-pool的Jar文件。也可以直接继承 org.springframework.aop.target.AbstractPoolingTargetSource来支持其它池API。

下面是一个配置的例子:

<bean id="businessObjectTarget" class="com.mycompany.MyBusinessObject" singleton="false"> ... properties omitted </bean> <bean id="poolTargetSource" class="org.springframework.aop.target.CommonsPoolTargetSource"> <property name="targetBeanName"><value>businessObject</value></property> <property name="maxSize"><value>25</value></property> </bean> <bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean" > <property name="targetSource"><ref local="poolTargetSource"/></property> <property name="interceptorNames"><value>myInterceptor</value></property> </bean>

注意例子中的目标对象“businessObjectTarget”必须是prototype。这样在 PoolingTargetSource的实现在扩大池容量的时候可以创建目标的新实例。关于这些属性的 信息可以参考AbstractPoolingTargetSource和子类的Javadoc。maxSize是最基本的属性, 被保证总是存在。

在这种情况下,名字为“myInterceptor”的拦截器需要定义在同一个IoC上下文中。但是,并不一定需要 指定拦截器也用池。如果你仅需要池,并且没有其它通知,可以根本不设置属性interceptorNames。

也可以配置Spring以便可以将任何池化的对象转换类型为 org.springframework.aop.target.PoolingConfig接口。通过这个接口的一个引入,可以得到 配置信息和池的当前大小。你需要这样定义一个advisor:

<bean id="poolConfigAdvisor" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="target"><ref local="poolTargetSource" /></property> <property name="targetMethod"><value>getPoolingConfigMixin</value></property> </bean>

通过调用AbstractPoolingTargetSource类上的方法,可以得到这个advisor, 因此使用MethodInvokingFactoryBean。这个advisor的名字(“poolConfigAdvisor”)必须在暴露池化对象的 This advisor is obtained by calling a convenience method on the ProxyFactoryBean中的拦截器名字列表中。

这个类型转换就象下面:

PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject"); System.out.println("Max pool size is " + conf.getMaxSize());

池化无状态业务对象并不是总是必要的。我们不认为这是缺省选择,因为大多数无状态对象自然就是线程 安全的,如果资源被缓存,实例池化会有问题。
简单的池也可以使用自动代理。任何自动代理生成器都可以设置TargetSources。

Prototype目标源
设置“prototype”目标源和支持池的目标源类似。在每次方法调用的时候都会创建一个新的目标实例。 虽然在现代JVM中创建对象的代价不是很高,但是装配新对象的代价可能更高(为了maz满足它的IoC依赖关系)。 因此没有好的理由不应该使用这个方法。

为了这么做,你可以修改上面的的poolTargetSource定义,就向下面一样。

<bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource"> <property name="targetBeanName"><value>businessObject</value></property> </bean>


只有一个属性:目标bean的名字。在TargetSource实现中使用继承是为了保证命名的一致性。就象支持池的 目标源一样,目标bean必须是一个prototype的bean定义。

0
相关文章