@Scope(“prototype“)

@Scope("prototype") 是 Spring 框架中用于定义 Bean 作用域的注解之一,它的主要作用是将一个 Bean 定义成 原型作用域Prototype Scope)。在原型作用域下,每次从 Spring 容器中请求这个 Bean 时,都会创建一个新的实例。


@Scope("prototype") 的作用

  1. 默认作用域:单例(Singleton Scope)

    在 Spring 中,Bean 的默认作用域是单例(@Scope("singleton")),意味着整个 Spring 容器中只会创建一个该 Bean 的实例,无论获取该 Bean 的次数有多少,都会返回同一个实例。

  2. 原型作用域(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 具有以下特点:

  1. 每次请求都会创建一个新实例

    • 无论通过调用 ApplicationContext#getBean() 方法,还是通过注入方式(例如通过 @Autowired)获取原型作用域的 Bean,每次都会创建一个全新的实例。
  2. 不受 Spring 容器的完全管理

    • Spring 容器只负责创建和返回原型作用域的 Bean,但不会对其进行后续的生命周期管理(例如,不会销毁它)。
    • 开发者需要负责清理(销毁)原型作用域的 Bean(如果需要)。
  3. 适用场景

    • 原型作用域适用于需要「短生命周期」的对象,或者需要生成多个实例的场景,例如:
      • 操作中需要临时创建对象(如一个用户操作的上下文)。
      • 在某些场景中,您需要一个特定的对象,每次使用时都需要是全新的(而不是共享的)。

对比 @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

分析:

  1. PrototypeBean 的实例在 Spring 容器初始化时并未被创建(不同于单例作用域)。
  2. 每次调用 context.getBean(PrototypeBean.class) 时都会创建一个新实例。
  3. 如果将一个原型作用域的 Bean 注入到单例作用域的 Bean 中,Spring 容器只会注入 一个实例(因为依赖注入通常发生在容器启动时),后续不会动态更新。

在原型作用域中动态获取实例

如果您需要在运行时动态获取新的原型实例,可以通过以下方式解决:

  1. 使用 @Lookup 注解

    在单例作用域的 Bean 中通过 @Lookup 动态注入原型实例(如之前的例子)。

  2. 手动调用 ApplicationContext#getBean()

    手动从容器中获取新的原型实例。

  3. 使用 ObjectFactoryProvider

    通过 ObjectFactoryjavax.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,销毁和清理需要开发者自行处理。
相关推荐
会员源码网1 小时前
数字格式化陷阱:如何优雅处理 NumberFormatException
java
孔明click332 小时前
Sa-Token v1.45.0 发布 🚀,正式支持 Spring Boot 4、新增 Jackson3/Snack4 插件适配
java·sa-token·开源·springboot·登录·权限认证
程序猿阿越2 小时前
Kafka4源码(二)创建Topic
java·后端·源码阅读
悟空码字2 小时前
Spring Boot 整合 MongoDB 最佳实践:CRUD、分页、事务、索引全覆盖
java·spring boot·后端
省长2 小时前
Sa-Token v1.45.0 发布 🚀,正式支持 Spring Boot 4、新增 Jackson3/Snack4 插件适配
java·后端·开源
NE_STOP3 小时前
MyBatis-动态sql与高级映射
java
后端AI实验室3 小时前
我把同一个需求分别交给初级程序员、高级程序员和AI,结果让我沉默了
java·ai
sTone873753 小时前
web后端开发概念: VO 和 PO
java·后端·架构
SimonKing4 小时前
JetBrains+Qoder变身Agentic 编码平台,媲美Cursor、Trae等AI编程平台
java·后端·程序员
Seven975 小时前
NIO:解开非阻塞I/O高并发编程的秘密
java