Spring Framework原型作用域:每次获取新实例
【免费下载链接】spring-framework 项目地址: https://gitcode.com/gh_mirrors/spr/spring-framework
你是否遇到过这样的问题:在Spring应用中,多次获取同一个Bean却得到了相同的实例,导致状态混乱?Spring Framework的原型作用域(Prototype Scope)正是解决这一问题的关键。本文将详细介绍原型作用域的概念、使用方法和注意事项,帮助你在实际开发中正确应用这一特性。
什么是原型作用域
在Spring Framework中,Bean的作用域定义了Bean实例的生命周期和创建方式。原型作用域是其中一种常用的作用域,它的特点是每次从Spring容器中获取Bean时,都会创建一个新的实例。
与默认的单例作用域(Singleton)不同,原型作用域的Bean不会被Spring容器缓存,每次请求都会创建新的对象。这使得原型作用域非常适合那些包含状态且需要频繁创建新实例的场景。
Spring Framework的作用域注解@Scope定义在spring-context/src/main/java/org/springframework/context/annotation/Scope.java文件中。该注解有两个主要属性:value(或scopeName)和proxyMode。
原型作用域的使用方法
使用注解配置原型作用域
要将一个Bean定义为原型作用域,只需在类级别或方法级别添加@Scope注解,并将其值设置为ConfigurableBeanFactory.SCOPE_PROTOTYPE。
import org.springframework.context.annotation.Scope;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.stereotype.Component;
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBean {
// Bean的实现代码
}
也可以直接使用字符串"prototype"作为作用域的值:
@Component
@Scope("prototype")
public class PrototypeBean {
// Bean的实现代码
}
对于使用@Bean注解定义的Bean,可以在方法级别应用@Scope注解:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
@Configuration
public class AppConfig {
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public PrototypeBean prototypeBean() {
return new PrototypeBean();
}
}
获取原型Bean的实例
在应用中获取原型Bean的方式与获取其他作用域的Bean相同,可以使用@Autowired注解或通过ApplicationContext的getBean()方法。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ClientBean {
@Autowired
private PrototypeBean prototypeBean;
public void usePrototypeBean() {
// 使用prototypeBean
// 注意:这里的prototypeBean实例可能不是每次调用都新创建的,详见后续说明
}
}
或者通过ApplicationContext获取:
import org.springframework.context.ApplicationContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ClientBean {
@Autowired
private ApplicationContext context;
public void usePrototypeBean() {
PrototypeBean prototypeBean = context.getBean(PrototypeBean.class);
// 使用prototypeBean,每次调用getBean()都会得到新实例
}
}
原型作用域的工作原理
原型Bean的创建过程
当Spring容器接收到获取原型Bean的请求时,会执行以下步骤:
- 检查Bean定义,确认作用域为原型
- 创建新的Bean实例
- 应用Bean的属性值(依赖注入)
- 调用初始化回调方法(如
@PostConstruct注解的方法) - 返回新创建的Bean实例
与单例Bean不同,Spring容器不会保留原型Bean的引用,也不会调用销毁回调方法(如@PreDestroy注解的方法)。原型Bean的生命周期管理完全由客户端负责。
原型作用域的代理模式
当原型Bean被其他Bean依赖时,需要注意依赖注入的时机。默认情况下,依赖注入发生在容器启动时(对于单例Bean),这意味着原型Bean的实例只会被创建一次,并注入到依赖它的Bean中。
为了解决这个问题,可以使用作用域代理(Scoped Proxy)。通过设置@Scope注解的proxyMode属性,可以为原型Bean创建一个代理对象,每次访问该代理对象时,都会获取一个新的原型Bean实例。
import org.springframework.context.annotation.Scope;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.stereotype.Component;
import org.springframework.context.annotation.ScopedProxyMode;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PrototypeBean {
// Bean的实现代码
}
ScopedProxyMode有以下几种取值:
DEFAULT:默认值,通常表示不创建代理,除非在组件扫描时配置了默认代理模式NO:不创建代理INTERFACES:创建基于接口的JDK动态代理TARGET_CLASS:创建基于类的CGLIB代理
原型作用域的应用场景
原型作用域适用于以下场景:
- 有状态的Bean:当Bean需要维护状态,且不同的使用场景需要独立的状态时
- 线程不安全的Bean:对于线程不安全的对象,使用原型作用域可以避免多线程共享实例导致的问题
- 频繁创建和销毁的对象:对于需要频繁创建和销毁的轻量级对象,原型作用域可以提供更好的性能
例如,在Web应用中,每个请求都可能需要一个新的服务对象来处理请求数据,这时就可以将服务Bean定义为原型作用域。
原型作用域的注意事项
与单例Bean的依赖关系
当原型Bean被单例Bean依赖时,需要特别注意。默认情况下,单例Bean在容器启动时被创建,此时依赖的原型Bean也会被创建一次并注入到单例Bean中。后续单例Bean再访问该原型Bean时,使用的仍然是最初注入的那个实例,而不是新的实例。
要解决这个问题,可以使用以下几种方法:
- 如上所述,使用作用域代理
- 直接通过
ApplicationContext或BeanFactory获取原型Bean - 使用
ObjectFactory或Provider来延迟获取原型Bean
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ClientBean {
@Autowired
private ObjectFactory<PrototypeBean> prototypeBeanFactory;
public void usePrototypeBean() {
PrototypeBean prototypeBean = prototypeBeanFactory.getObject();
// 每次调用getObject()都会得到新的原型Bean实例
}
}
原型Bean的生命周期管理
Spring容器只负责创建原型Bean,而不负责销毁它们。原型Bean的销毁和资源释放需要由客户端代码手动管理。如果原型Bean持有重要资源,未正确释放可能导致资源泄漏。
性能考虑
虽然原型Bean的创建开销通常很小,但在需要频繁创建大量原型Bean的场景下,仍可能对性能产生影响。此时应考虑对象池或其他优化策略。
原型作用域与其他作用域的比较
Spring Framework提供了多种Bean作用域,除了原型作用域,常用的还有:
- 单例(Singleton):默认作用域,整个应用中只创建一个Bean实例
- 请求(Request):Web应用中,每个HTTP请求创建一个新实例
- 会话(Session):Web应用中,每个用户会话创建一个新实例
- 应用(Application):Web应用中,在ServletContext范围内只创建一个实例
以下是原型作用域与单例作用域的主要区别:
| 特性 | 原型作用域 | 单例作用域 |
|---|---|---|
| 实例数量 | 每次请求创建新实例 | 整个应用一个实例 |
| 容器管理 | 只负责创建,不负责销毁 | 全程管理生命周期 |
| 状态共享 | 不共享状态,每个实例独立 | 所有使用者共享一个实例 |
| 性能开销 | 创建实例的开销较高 | 实例创建开销一次,后续使用开销低 |
总结
原型作用域是Spring Framework中一种重要的Bean作用域,它允许每次请求时创建新的Bean实例,非常适合处理有状态的对象或需要频繁创建的对象。正确使用原型作用域可以提高应用的灵活性和可维护性,但也需要注意与单例Bean的依赖关系和生命周期管理问题。
在实际开发中,应根据具体的业务场景选择合适的Bean作用域,并遵循Spring的最佳实践来管理Bean的生命周期。通过合理使用原型作用域,可以让你的Spring应用更加健壮和高效。
希望本文能帮助你深入理解Spring Framework的原型作用域,并在实际项目中正确应用这一特性。如果你想了解更多关于Spring作用域的知识,可以参考Spring官方文档中的Bean Scopes章节。
【免费下载链接】spring-framework 项目地址: https://gitcode.com/gh_mirrors/spr/spring-framework
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



