Spring Bean的作用域有singleton、prototype、request、session、application、websocket。
singleton
singleton就是常见的单例模式的一种实现,默认情况下bean的作用域就是singleton,所以无需做任何配置。
需要注意的是这里所指的单例并不是应用程序中的单例,而是在当前bean容器中是单例的。当bean被定义为单例时,spring容器中会共享这个bean实例。
prototype
当bean的scope设定为prototype时,如果这个bean需要被引用(注入到某个bean中或者被getBean()方式查找),spring容器都会根据当前bean定义创建一个全新的bean实例对象返回。
实现方式
- 通过xml配置
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
- 通过@Scope
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
- 通过BeanDefinitionBuilder#setScope
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition (User.class);
builder.addPropertyValue ("name", "bd-user")
.setScope (BeanDefinition.SCOPE_PROTOTYPE);
request & session & application
这几个作用域只能web环境中有效,如果是在非web的spring环境使用这些作用域,容器会抛出IllegalStateException。
request
被定义成request作用域,即每次请求都会创建一个新的bean实例。
session
被定义成session作用域,即在同一个session环境中共享bean实例
application
被定义成application作用域,即在servlet上下文环境中共享bean实例
singleton类型bean注入prototype类型bean问题
因为bean的依赖关系是在实例化时解析完成的,所以singleton类型bean中的注入prototype类型bean的过程也是只有一次,那么其实每次使用的都是同一个prototype类型bean对象。
如果想singleton类型bean在运行时重新获取新的prototype类型bean实例,spring提供了method injection的方式解决这个问题 【方法注入非这次的学习重点,暂不过多论述】。
源码解析
源码入口,作用域判断的主要逻辑在AbstractBeanFactory的doGetBean:
//调用栈
AbstractApplicationContext#refresh
->AbstractApplicationContext#finishBeanFactoryInitialization
->AbstractApplicationContext#preInstantiateSingletons
->DefaultListableBeanFactory#preInstantiateSingletons
->AbstractBeanFactory#getBean
->AbstractBeanFactory#doGetBean
-
下图是作用域为Singleton的处理入口
-
下图是作用域为Prototype的处理入口
-
下图是其他作用域的处理入口,如request、session、application、websocket、自定义作用域
request&session的处理入口
-
request和session的处理入口是AbstractRequestAttributesScope#get
application的处理入口
-
application的处理入口时ServletContextScope#get