Spring系列之Bean的作用域、生命周期
概述
作用域
Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式。Spring Bean的常见作用域,后3种作用域,只适用于Spring MVC框架:
- singleton:单例作用域
- prototype:原型作用域(多例作用域)
- request:请求作用域
- session:会话作用域
- application:也叫global,全局作用域
singleton
(Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
描述:该作用域下的 Bean 在 IoC 容器中只存在一个实例:获取 Bean(即通过 applicationContext.getBean等方法获取)及装配 Bean(即通过 @Autowired 注入)都是同一个对象。
场景:通常无状态的 Bean 使用该作用域。无状态表示 Bean 对象的属性状态不需要更新。
备注:Spring 默认选择该作用域。singleton 单例作用域,就表示 Bean 在整个 Spring 中只有一份,它是全局共享的,当有人修改了这个值之后,那么另一个人读取到的就是被修改后的值。
prototype
Scopes a single bean definition to any number of object instances.
描述:每次对该作用域下的 Bean 的请求都会创建新的实例:获取 Bean(即通过applicationContext.getBean
等方法获取)及装配 Bean(即通过 @Autowired 注入)都是新的对象实例。
场景:通常有状态的 Bean 使用该作用域。
request
Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
描述:每次 Http 请求会创建新的 Bean 实例,类似于 prototype。
场景:一次 Http 的请求和响应的共享 Bean。
备注:限定 Spring MVC 框架中使用。
session
Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
描述:在一个 Http Session 中,定义一个 Bean 实例。
场景:用户会话的共享 Bean, 比如:记录一个用户的登陆信息。
备注:限定 Spring MVC 框架中使用。
application
Scopes a single bean definition to the lifecycle of a global HTTP Session. Typically only valid when used in a portlet context. Only valid in the context of a web-aware Spring ApplicationContext.
描述:在一个 Http Servlet Context 中,定义一个 Bean 实例。
场景:Web 应用的上下文信息,比如:记录一个应用的共享信息。
备注:限定 Spring MVC 框架中使用。
websocket:bean被定义为在websocket的生命周期中复用一个单例对象,不太常见。
可通过@Scope
设置Bean的作用域:
@Scope("prototype")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Scope(WebApplicationContext.SCOPE_APPLICATION)
singleton & prototype
如果一个bean被声明为单例的时候,在处理多次请求的时候在Spring容器里只实例化出一个bean,后续的请求都公用这个对象,这个对象会保存在一个map里面。当有请求来的时候会先从缓存(map)里查看有没有,有的话直接使用这个对象,没有的话才实例化一个新的对象,所以这是个单例的。但是对于原型(prototype)bean来说当每次请求来的时候直接实例化新的bean,没有缓存以及从缓存查的过程。
AbstractBeanFactory.doGetBean()
源码找到证据:
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
} else {
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
}
Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex);
}
}
源码跟进去,DefaultSingletonBeanRegistry
里面定义有如下Map:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
singleton bean优势,也是Spring为啥默认把bean设计成单例:
- 减少新生成实例的消耗
新生成实例消耗包括两方面,第一,spring会通过反射或者cglib来生成bean实例这都是耗性能的操作,其次给对象分配内存也会涉及复杂算法。 - 减少JVM垃圾回收
由于不会给每个请求都新生成bean实例,所以自然回收的对象少了。 - 可以快速获取到bean
因为单例的获取bean操作除了第一次生成之外其余的都是从缓存里获取的所以很快。
劣势
不能保证线程安全。所有请求都共享一个bean实例,有状态bean在并发场景下有线程安全问题。原型bean,因为给每个请求都新创建实例,则不会有这样问题(但也有例外,如被单例bean依赖)。
生命周期
Bean的生命周期是指:Bean 在 Spring(IoC)中从创建到销毁的整个过程,主要包含5部分:
- 实例化:为 Bean 分配内存空间;
- 设置属性:将当前类依赖的 Bean 属性,进行注入和装配;
- 初始化:
- 执行各种通知;
- 执行初始化的前置方法;
- 执行初始化方法;
- 执行初始化的后置方法。
- 使用 Bean:在程序中使用 Bean 对象;
- 销毁 Bean:将 Bean 对象进行销毁操作。
完整涉及到接口和类的详细版的生命周期:
- Bean容器找到配置文件中Spring Bean的定义
- Bean容器利用Java Reflection API创建一个Bean的实例
- 如果涉及到一些属性值,利用set方法设置一些属性值
- 如果Bean实现BeanNameAware接口,调用setBeanName()方法,传入Bean的名字
- 如果Bean实现BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例
- 如果Bean实现了BeanFactoryAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例
- 与上面的类似,如果实现其他*Aware接口,就调用相应的方法
- 如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执行postProcessBeforeInitialization()方法
- 如果Bean实现InitializingBean接口,执行afterPropertiesSet()方法
- 如果Bean在配置文件中的定义包含init-method属性,执行指定的方法
- 如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执行postProcessAfterInitialization()方法
- 当要销毁Bean时,如果Bean实现DisposableBean接口,执行destroy()方法
- 当要销毁Bean的时候,如果Bean在配置文件中的定义包含destroy-method属性,执行指定的方法
实例化只是给 Bean 分配内存空间,而初始化则是将程序的执行权,从系统级别转换到用户级别,并开始执行用户业务代码。
singleton bean被定义为一个单例对象,该对象的生命周期是与Spring IOC容器一致的(但在第一次被注入时才会创建)
方法:
setup,在容器加载bean时被调用,teardown在容器卸载类时被调用。bean标签有两个重要属性(init-method和destroy-method),可用于定制初始化和注销方法,有相应的注解(@PostConstruct和@PreDestroy)。
Aware
有时需要在Bean的初始化中使用Spring框架自身的一些对象来执行一些操作,如获取ServletContext的参数,ApplicaitionContext中的BeanDefinition的名字,Bean在容器中的名字等。为了让Bean可以获取到框架自身的一些对象,Spring提供一组名为*Aware
的接口。
这些接口均继承于org.springframework.beans.factory.Aware
标记接口,并提供一个将由Bean实现的set*
方法,Spring通过基于setter的依赖注入方式使相应的对象可以被Bean使用。这些接口是利用观察者模式实现的,类似于servlet listeners。
Aware接口:
- ApplicationContextAware:获得ApplicationContext对象,可以用来获取所有Bean definition的名字
- BeanFactoryAware:获得BeanFactory对象,可以用来检测Bean的作用域
- BeanNameAware:获得Bean在配置文件中定义的名字
- ResourceLoaderAware:获得ResourceLoader对象,可以获得classpath中某个文件
- ServletContextAware:在一个MVC应用中可以获取ServletContext对象,可以读取context中的参数
- ServletConfigAware:在一个MVC应用中可以获取ServletConfig对象,可以读取config中的参数
代码:
@Slf4j
public class DemoService implements ApplicationContextAware,
ApplicationEventPublisherAware, BeanClassLoaderAware, BeanFactoryAware,
BeanNameAware, EnvironmentAware, ImportAware, ResourceLoaderAware{
@Override
public void setBeanClassLoader(ClassLoader classLoader){
log.info("执行setBeanClassLoader,ClassLoader Name = " + classLoader.getClass().getName());
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
log.info("执行setBeanFactory,setBeanFactory:: giraffe bean singleton=" + beanFactory.isSingleton("giraffeService"));
}
@Override
public void setBeanName(String s) {
log.info("执行setBeanName:: Bean Name defined in context=" + s);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("执行setApplicationContext:: Bean Definition Names=" + Arrays.toString(applicationContext.getBeanDefinitionNames()));
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
log.info("执行setApplicationEventPublisher");
}
@Override
public void setEnvironment(Environment environment) {
log.info("执行setEnvironment");
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
Resource resource = resourceLoader.getResource("classpath:spring-beans.xml");
log.info("执行setResourceLoader:: Resource File Name=" + resource.getFilename());
}
@Override
public void setImportMetadata(AnnotationMetadata annotationMetadata) {
log.info("执行setImportMetadata");
}
}
BeanPostProcessor
上面的*Aware接口是针对某个实现这些接口的Bean定制初始化的过程,Spring同样可以针对容器中的所有Bean,或者某些Bean定制初始化过程,只需提供一个实现BeanPostProcessor接口的类即可。 该接口中包含两个方法,postProcessBeforeInitialization和postProcessAfterInitialization。 postProcessBeforeInitialization方法会在容器中的Bean初始化之前执行, postProcessAfterInitialization方法在容器中的Bean初始化之后执行:
@Slf4j
@Componet
public class DemoBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
log.info("执行BeanPostProcessor的postProcessBeforeInitialization方法,beanName=" + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
log.info("执行BeanPostProcessor的postProcessAfterInitialization方法,beanName=" + beanName);
return bean;
}
}
问题:
能不能先执行初始化再执行设置属性呢?步骤2和3的执行顺序交换一下?
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具