深入解读Spring Framework IoC容器(第七弹:不同作用域Bean的依赖)

通常情况下,我们都是把一个单例bean注入到另一个单例bean,这不会有作用域的问题。
但是不同作用域的Spring Bean之间互相依赖,如果不做特殊定义,则只能在实例化Spring Bean时注入其所依赖的其他Spring Bean。比如以下定义:

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

<bean id="userManager" class="com.foo.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>

在这个例子中,将会话域的userPreferences bean注入进了单例域userManager。userManager是个单例bean,它在每个Spring容器中只会被初始化一次,它的依赖对象,也就是userPreferences bean也只会被注入一次。这就意味着userManager每次操作的都是在最开始注入进来的那一个userPreferences对象。

这显然不是我们想要的效果。

当我们把一个短生命周期的bean注入至一个长生命周期的bean时,我们希望有个唯一的userManager单例对象,在每个HTTP Session的生命周期内,都能有一个专门的userPreferences对象供userManager使用。

AOP代理注入

<!-- 将一个HTTP Session bean暴露为一个代理bean -->
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
    <!-- 通知Spring容器去代理这个bean -->
    <aop:scoped-proxy/>
</bean>

<!-- 将上述bean的代理注入到一个单例bean -->
<bean id="userService" class="com.foo.SimpleUserService">
    <property name="userPreferences" ref="userPreferences"/>
</bean>

使用以上配置,Spring容器会创建一个代理类, 使之与UserPreferences 类实现相同的接口,理论上这个代理类也是一个UserPreferences对象,并能根据作用域去获得真正的UserPreferences对象。Spring容器将这个代理类的对象注入到userManager,但是userManager并不清楚它获得的UserPreferences其实是个代理。当UserManager实例调用注入的UserPreferences对象的某个方法时,实际上调用的是代理对象的方法。然后,代理对象从HTTP Session中获取,并将方法调用委派给真正的UserPreferences对象。

默认情况下,对于使用了<aop:scoped-proxy/>元素的bean,Spring容器会创建一个基于CGLIB代理机制的代理类。不过CGLIB代理只能拦截public方法,所以不要在代理类上调用非public方法。

Lookup方法注入

package fiona.apple;

// CommandManager类没有任何的Spring依赖
public abstract class CommandManager {

    public Object process(Object commandState) {
        // 取得一个Command的新实例
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    // 定义为抽象方法,该方法返回的是command bean的实例,由Spring容器自动复写和调用
    protected abstract Command createCommand();
}
<!-- 原型的bean -->
<bean id="command" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- 注入相关的依赖 -->
</bean>

<bean id="commandManager" class="fiona.apple.CommandManager">
    <!-- commandManager bean在需要一个新的command bean实例时会调用createCommand()方法 -->
    <lookup-method name="createCommand" bean="command"/>
</bean>

Lookup方法注入的内部机制是Spring利用了CGLIB库在运行时生成二进制代码的功能,通过动态创建Lookup方法bean的子类从而达到复写Lookup方法的目的。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值