在 Spring 框架中,prototype 是 Bean 的作用域(Scope)之一。它表示每次从 Spring 容器中获取该 Bean 时,都会创建一个新的实例。这与默认的 singleton 作用域不同,singleton 作用域下,Spring 容器中只会存在一个 Bean 实例。
1. prototype 作用域的特点
每次请求都会创建新实例:每次通过 ApplicationContext.getBean() 或依赖注入获取 Bean 时,都会创建一个新的实例。
生命周期不由 Spring 管理:prototype Bean 的初始化回调(如 @PostConstruct)会执行,但销毁回调(如 @PreDestroy)不会执行,因为 Spring 不会管理 prototype Bean 的生命周期。
适合有状态的 Bean:如果 Bean 需要维护状态,或者每次使用时需要独立的实例,可以使用 prototype 作用域。
2. 配置 prototype 作用域
可以通过以下方式配置 prototype 作用域:
2.1 使用注解
在类上使用 @Scope 注解:
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("prototype") // 指定作用域为 prototype
public class MyPrototypeBean {
public MyPrototypeBean() {
System.out.println("MyPrototypeBean created!");
}
}
2.2 使用 XML 配置
在 XML 配置文件中定义 Bean 时指定 scope 属性:
运行 HTML
3. 使用 prototype Bean
在 Spring 中,prototype Bean 的使用方式与 singleton Bean 相同,但每次获取时都会创建一个新实例。
3.1 通过依赖注入
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MyService {
private final MyPrototypeBean prototypeBean;
@Autowired
public MyService(MyPrototypeBean prototypeBean) {
this.prototypeBean = prototypeBean;
System.out.println("MyService created with prototypeBean: " + prototypeBean);
}
}
3.2 通过 ApplicationContext 获取
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class MyService {
private final ApplicationContext context;
public MyService(ApplicationContext context) {
this.context = context;
}
public void usePrototypeBean() {
MyPrototypeBean bean1 = context.getBean(MyPrototypeBean.class);
MyPrototypeBean bean2 = context.getBean(MyPrototypeBean.class);
System.out.println("bean1 == bean2: " + (bean1 == bean2)); // false
}
}
4. prototype 与 singleton 的区别
特性
prototype
singleton
实例数量
每次请求创建一个新实例
整个容器中只有一个实例
生命周期管理
Spring 不管理销毁阶段
Spring 管理完整的生命周期
适用场景
有状态的 Bean,需要独立实例的场景
无状态的 Bean,共享实例的场景
性能
每次请求都会创建新实例,性能较低
单例模式,性能较高
5. 注意事项
prototype Bean 注入到 singleton Bean 中:
如果 prototype Bean 被注入到 singleton Bean 中,prototype Bean 的行为仍然是单例的,因为 singleton Bean 只会初始化一次。
解决方法:使用 @Lookup 注解或 Provider 接口。
5.1 使用 @Lookup 注解
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.stereotype.Component;
@Component
public abstract class MyService {
@Lookup
public abstract MyPrototypeBean getPrototypeBean();
public void usePrototypeBean() {
MyPrototypeBean bean1 = getPrototypeBean();
MyPrototypeBean bean2 = getPrototypeBean();
System.out.println("bean1 == bean2: " + (bean1 == bean2)); // false
}
}
5.2 使用 Provider 接口
import javax.inject.Provider;
import org.springframework.stereotype.Component;
@Component
public class MyService {
private final Provider
public MyService(Provider
this.prototypeBeanProvider = prototypeBeanProvider;
}
public void usePrototypeBean() {
MyPrototypeBean bean1 = prototypeBeanProvider.get();
MyPrototypeBean bean2 = prototypeBeanProvider.get();
System.out.println("bean1 == bean2: " + (bean1 == bean2)); // false
}
}
6. 总结
prototype 作用域适用于需要每次创建新实例的场景。
通过 @Scope("prototype") 或 XML 配置可以定义 prototype Bean。
注意 prototype Bean 注入到 singleton Bean 中的问题,可以使用 @Lookup 或 Provider 解决。