在 Spring 中,prototype
Bean 注入 prototype
Bean 和 singleton
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
也会是一个全新的实例。 - 因为
PrototypeBeanB
是prototype
作用域的,所以每次创建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
方法注入、ObjectFactory
或Provider
。
示例 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 方法、ObjectFactory 或 Provider 实现动态获取新的 prototype 实例 |
4. 结论
- 当
prototype
Bean 注入prototype
Bean 时,Spring 的默认行为是符合预期的,每次使用时都会创建新的实例。 - 当
singleton
Bean 需要注入prototype
Bean 时,必须采用@Lookup
、ObjectFactory
或Provider
方式来确保每次都能获取新的prototype
实例,否则默认情况下只会创建一个实例。