原型Bean在单例中“变单例”是因依赖注入仅在单例初始化时执行一次,后续始终复用该实例;正确解法是使用ObjectFactory/Provider延迟获取、@Lookup方法或ApplicationContext.getBean()按需创建新实例。

单例 Bean 注入原型 Bean 后,原型实例在单例初始化时就被固定下来,后续调用始终复用同一个对象——这不是 Bug,而是 Spring 依赖注入时机决定的必然结果。关键在于:原型 Bean 的“每次新建”只在 容器主动获取时(如 getBean())才触发,而自动注入(@Autowired)属于一次性填充,不重复执行。
为什么原型 Bean 在单例里“变单例”了?
Spring 容器启动时创建单例 Bean,会完整走完实例化 → 依赖注入 → 初始化流程。此时遇到 @Autowired PrototypeBean,容器就调用一次 getBean(PrototypeBean.class) 创建新实例并注入。之后单例 Bean 一直持有这个引用,再不会重新获取。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
换句话说:注入动作只发生一次,不是每次方法调用都重取。所以即使 PrototypeBean 标了 @Scope("prototype"),它在单例中也成了“静态快照”。
三种可靠解法,按推荐顺序排列
-
使用
ObjectFactory或Provider延迟获取
把直接注入改为注入工厂,每次需要时才调用getObject()或get()。Spring 会为它们生成代理,确保每次返回新实例。
示例:@Autowired private ObjectFactory<PrototypeBean> prototypeBeanFactory;PrototypeBean bean = prototypeBeanFactory.getObject(); // 每次都是新对象 -
用
@Lookup方法让容器动态替换
在单例类中声明一个抽象方法,加上@Lookup注解,Spring 会在运行时代理该方法,每次调用都返回新原型实例。
注意:该类不能是 final,方法不能是 private/static。 -
手动通过
ApplicationContext获取
在方法体内注入ApplicationContext,需要时调用context.getBean(PrototypeBean.class)。
缺点:耦合容器 API,不利于测试;需确保 context 非 null 且未关闭。
哪些做法无效或危险?
- 仅加
@Scope("prototype")但继续用@Autowired字段注入 —— 不起作用 - 把单例 Bean 改成 prototype —— 解决了问题但代价过大(失去复用、增加 GC 压力、可能引发其他依赖异常)
- 在单例中缓存原型 Bean 实例再手动 new —— 绕过 Spring 容器,丢失依赖注入、生命周期回调(如
@PostConstruct)、AOP 代理等能力
核心原则是:让“获取新实例”的动作发生在运行时、按需触发,而不是固化在初始化阶段。选 ObjectFactory 或 Provider 是最轻量、最符合 Spring 设计哲学的方式。

















