@Scope("prototype")
是 Spring 框架中用于定义 Bean 作用域的注解之一,它的主要作用是将一个 Bean 定义成 原型作用域 (Prototype Scope
)。在原型作用域下,每次从 Spring 容器中请求这个 Bean 时,都会创建一个新的实例。
@Scope("prototype")
的作用
-
默认作用域:单例(Singleton Scope)
在 Spring 中,Bean 的默认作用域是单例(
@Scope("singleton")
),意味着整个 Spring 容器中只会创建一个该 Bean 的实例,无论获取该 Bean 的次数有多少,都会返回同一个实例。 -
原型作用域(Prototype Scope)
如果将 Bean 标记为
@Scope("prototype")
,则表示该 Bean 是原型作用域。每次通过 Spring 容器获取这个 Bean 时,都会创建一个全新的实例,这与单例作用域的行为完全不同。
如何使用 @Scope("prototype")
java
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("prototype") // 标记为原型作用域
public class PrototypeBean {
public PrototypeBean() {
System.out.println("PrototypeBean instance created!");
}
}
@Scope("prototype")
的生命周期
与单例作用域的 Bean 不同,原型作用域的 Bean 具有以下特点:
-
每次请求都会创建一个新实例:
- 无论通过调用
ApplicationContext#getBean()
方法,还是通过注入方式(例如通过@Autowired
)获取原型作用域的 Bean,每次都会创建一个全新的实例。
- 无论通过调用
-
不受 Spring 容器的完全管理:
- Spring 容器只负责创建和返回原型作用域的 Bean,但不会对其进行后续的生命周期管理(例如,不会销毁它)。
- 开发者需要负责清理(销毁)原型作用域的 Bean(如果需要)。
-
适用场景:
- 原型作用域适用于需要「短生命周期」的对象,或者需要生成多个实例的场景,例如:
- 操作中需要临时创建对象(如一个用户操作的上下文)。
- 在某些场景中,您需要一个特定的对象,每次使用时都需要是全新的(而不是共享的)。
- 原型作用域适用于需要「短生命周期」的对象,或者需要生成多个实例的场景,例如:
对比 @Scope("singleton")
和 @Scope("prototype")
特性 | 单例作用域(Singleton) | 原型作用域(Prototype) |
---|---|---|
默认值 | 是 | 否 |
实例数量 | Spring 容器中只有一个实例 | 每次请求都会创建一个新实例 |
实例管理 | 由 Spring 容器全权负责(包括初始化和销毁) | 仅由 Spring 容器负责创建,不负责销毁 |
适用场景 | 适用于共享的全局对象(如服务类或工具类等) | 适用于需要「短生命周期」或需要动态创建的对象 |
示例代码与行为分析
以下是一个完整示例,演示 @Scope("prototype")
的作用:
PrototypeBean 类
java
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("prototype")
public class PrototypeBean {
public PrototypeBean() {
System.out.println("PrototypeBean instance created!");
}
}
SingletonBean 类
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class SingletonBean {
@Autowired
private PrototypeBean prototypeBean1;
public void showPrototypeBean() {
System.out.println("PrototypeBean instance: " + prototypeBean1);
}
}
主方法
java
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext("com.example");
// 第一次获取 SingletonBean
SingletonBean singletonBean1 = context.getBean(SingletonBean.class);
singletonBean1.showPrototypeBean();
// 第二次获取 SingletonBean
SingletonBean singletonBean2 = context.getBean(SingletonBean.class);
singletonBean2.showPrototypeBean();
// 手动获取 PrototypeBean
PrototypeBean prototypeBean1 = context.getBean(PrototypeBean.class);
PrototypeBean prototypeBean2 = context.getBean(PrototypeBean.class);
System.out.println("PrototypeBean instances comparison: " + (prototypeBean1 == prototypeBean2)); // false
}
}
输出结果
plaintext
PrototypeBean instance created!
PrototypeBean instance: com.example.PrototypeBean@1a2b3c4
PrototypeBean instance: com.example.PrototypeBean@1a2b3c4
PrototypeBean instance created!
PrototypeBean instance created!
PrototypeBean instances comparison: false
分析:
PrototypeBean
的实例在 Spring 容器初始化时并未被创建(不同于单例作用域)。- 每次调用
context.getBean(PrototypeBean.class)
时都会创建一个新实例。 - 如果将一个原型作用域的 Bean 注入到单例作用域的 Bean 中,Spring 容器只会注入 一个实例(因为依赖注入通常发生在容器启动时),后续不会动态更新。
在原型作用域中动态获取实例
如果您需要在运行时动态获取新的原型实例,可以通过以下方式解决:
-
使用
@Lookup
注解在单例作用域的 Bean 中通过
@Lookup
动态注入原型实例(如之前的例子)。 -
手动调用
ApplicationContext#getBean()
手动从容器中获取新的原型实例。
-
使用
ObjectFactory
或Provider
通过
ObjectFactory
或javax.inject.Provider
提供动态获取实例的能力。
示例(使用 ObjectFactory
):
java
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class SingletonBean {
@Autowired
private ObjectFactory<PrototypeBean> prototypeBeanFactory;
public void showPrototypeBean() {
PrototypeBean prototypeBean = prototypeBeanFactory.getObject(); // 获取一个新的 PrototypeBean 实例
System.out.println("PrototypeBean instance: " + prototypeBean);
}
}
总结
@Scope("prototype")
意味着每次请求都会创建一个新的 Bean 实例。- 原型作用域适用于需要动态创建、短生命周期的对象。
- 需要注意的是,Spring 容器不会完全管理原型作用域的 Bean,销毁和清理需要开发者自行处理。