singlleton 的 bean 中注入 prototype 的bean

在 Spring 中,prototype Bean 注入 prototype Beansingleton Bean 注入 prototype Bean 是两个典型的场景,尤其在多线程、状态管理等复杂系统中,会有不同的行为和注意事项。

1. prototype Bean 中注入 prototype Bean

当一个 prototype Bean 注入另一个 prototype Bean 时,每次获取外部的 prototype Bean 实例时,Spring 会为该 Bean 创建一个新的实例。这种情况较为简单,因为 prototype 作用域的 Beans 都是动态生成的新实例。

特点:

  • 每个 prototype Bean 都是独立的,每次请求都会创建一个新对象。
  • Spring 管理 Bean 的创建和初始化,但不会管理 prototype Bean 的销毁。

示例:

@Component
@Scope("prototype")
public class PrototypeBeanA {
    // Prototype Bean A 的逻辑
}

@Component
@Scope("prototype")
public class PrototypeBeanB {
    
    @Autowired
    private PrototypeBeanA prototypeBeanA;

    public void doSomething() {
        // 每次使用的 prototypeBeanA 都是新的实例
        System.out.println(prototypeBeanA);
    }
}

结果:

  • 每次 PrototypeBeanB 被创建时,PrototypeBeanA 也会是一个全新的实例。
  • 因为 PrototypeBeanBprototype 作用域的,所以每次创建 PrototypeBeanB 时,PrototypeBeanA 都会重新注入一个新的实例。

2. singleton Bean 中注入 prototype Bean

这是一个常见的复杂场景,因为 singleton Bean 是单例,而 prototype Bean 是多例。一旦 singleton Bean 被创建,所有的依赖都将固定下来。如果在 singleton Bean 中直接注入 prototype Bean,那么这个 prototype Bean 实际上只会被创建一次,并且在整个应用程序的生命周期中都只会有这个唯一的实例,这显然与 prototype Bean 的设计初衷不符。

特点:

  • 问题:当 singleton Bean 注入 prototype Bean 时,注入的 prototype Bean 只会初始化一次,之后不会再创建新的实例。每次使用的都是同一个 prototype 实例。
  • 解决方法:为了确保 singleton Bean 每次使用 prototype Bean 时都能获取到新的 prototype 实例,必须使用一些特殊的技术,如 @Lookup 方法注入ObjectFactoryProvider

示例 1:直接注入 prototype Bean(错误方式)

@Component
public class SingletonBean {
    
    @Autowired
    private PrototypeBeanA prototypeBeanA;

    public void doSomething() {
        // 每次调用时都会使用同一个 prototypeBeanA 实例
        System.out.println(prototypeBeanA);
    }
}

在这个例子中,prototypeBeanA 只会被创建一次,因此每次调用 doSomething() 方法时,prototypeBeanA 都是同一个实例,这与 prototype 的预期不一致。

示例 2:使用 @Lookup 方法(正确方式)

@Lookup 注解告诉 Spring 每次调用这个方法时,都需要返回一个新的 prototype 实例。

@Component
public class SingletonBean {
    
    @Lookup
    public PrototypeBeanA getPrototypeBeanA() {
        // Spring 将返回新的 PrototypeBeanA 实例
        return null; // 这个返回值会被 Spring 动态代理替换
    }

    public void doSomething() {
        PrototypeBeanA prototypeBeanA = getPrototypeBeanA();
        System.out.println(prototypeBeanA); // 每次获取的都是新的实例
    }
}

示例 3:使用 ObjectFactory 注入

ObjectFactory 是 Spring 提供的一个工厂接口,用来在每次调用时动态获取 prototype Bean。

@Component
public class SingletonBean {
    
    @Autowired
    private ObjectFactory<PrototypeBeanA> prototypeBeanFactory;

    public void doSomething() {
        PrototypeBeanA prototypeBeanA = prototypeBeanFactory.getObject();
        System.out.println(prototypeBeanA); // 每次获取的都是新的实例
    }
}

示例 4:使用 Provider 注入

javax.inject.Provider 也提供类似的动态获取 prototype Bean 的功能。

@Component
public class SingletonBean {
    
    @Autowired
    private Provider<PrototypeBeanA> prototypeBeanProvider;

    public void doSomething() {
        PrototypeBeanA prototypeBeanA = prototypeBeanProvider.get();
        System.out.println(prototypeBeanA); // 每次获取的都是新的实例
    }
}

3. 总结对比

场景 prototype Bean 注入 prototype Bean singleton Bean 注入 prototype Bean
默认行为 每次注入时都会创建新的 prototype 实例 注入的 prototype Bean 只会创建一次(单例生命周期内只创建一次)
常见问题 行为符合预期,每次使用新的 prototype 实例 singleton Bean 会持有同一个 prototype 实例,违背 prototype 设计初衷
解决方案 默认情况下即可实现正确行为 使用 @Lookup 方法、ObjectFactoryProvider 实现动态获取新的 prototype 实例

4. 结论

  • prototype Bean 注入 prototype Bean 时,Spring 的默认行为是符合预期的,每次使用时都会创建新的实例。
  • singleton Bean 需要注入 prototype Bean 时,必须采用 @LookupObjectFactoryProvider 方式来确保每次都能获取新的 prototype 实例,否则默认情况下只会创建一个实例。
posted @ 2024-09-28 21:37  gongchengship  阅读(12)  评论(0编辑  收藏  举报