Bean默认有五个作用域:singleton、prototype、request、session和globalSession。

如下图所示:

类别

说明

singleton

Spring IoC 容器中只会存在一个共享的 Bean 实例,无论有多少个 Bean 引用它,始终指向同一对象。该模式在多线程下是不安全的。Singleton 作用域是 Spring 中的缺省作用域,也可以显示的将 Bean 定义为 singleton 模式

prototype

原型模式,每次通过 Spring 容器获取 prototype 定义的 bean 时,容器都将创建一个新的 Bean 实例,每个 Bean 实例都有自己的属性和状态,而 singleton 全局只有一个对象。根据经验,对有状态的bean使用prototype作用域,而对无状态的bean使用singleton 作用域。

request

在一次 Http 请求中,容器会返回该 Bean 的同一实例。而对不同的 Http 请求则会产生新的 Bean,而且该 bean 仅在当前 Http Request 内有效,当前 Http 请求结束,该 bean 实例也将会被销毁。该作用域仅适用于WebApplicationContext环境

session

在一次 Http Session 中,容器会返回该 Bean 的同一实例。而对不同的 Session 请求则会创建新的实例,该 bean 实例仅在当前 Session 内有效。同 Http 请求相同,每一次 session 请求创建新的实例,而不同的实例之间不共享属性,且实例仅在自己的 session 请求内有效,请求结束,则实例将被销毁。该作用域仅适用于WebApplicationContext环境

global Session

在一个全局的 Http Session 中,容器会返回该 Bean 的同一个实例,仅在使用portlet context 时有效

【1】Bean的作用域

① Singleton-默认值

容器初始时创建bean实例,在整个容器的生命周期内只创建这一个bean。如果不显示设置,默认值为Singleton。

当一个bean的作用域为singleton, 那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。

换言之,当把一个bean定义设置为singlton作用域时,Spring IoC容器只会创建该bean定义的唯一实例。

这个单一实例会被存储到单例缓存(singleton cache)中,并且所有针对该bean的后续请求和引用都将返回被缓存的对象实例。


如下示例,在beans.xml中定义了一个bean–Car,Car的构造器中打印了一句话。

创建容器-将会创建xml中的bean :

ApplicationContext aContext = 
new ClassPathXmlApplicationContext("beans-scope.xml");

测试多次使用getBean方法获取Bean :

@Test
public void testAddress1(){
ApplicationContext aContext =
new ClassPathXmlApplicationContext("beans-scope.xml");

Car car = (Car) aContext.getBean("car");
Car car2 = (Car) aContext.getBean("car");

System.out.println(car == car2);
}

控制台打印:

Car's construtors ..
true//两个对象相等

即,单实例情况下,同一个bean只存在唯一,不会重复。如new User(1,‘lily’,18),无论执行多少次,容器中只存在一个。

注意:Spring中管理的bean,如@Controller,@Service,@Repository等加了注解被扫描进容器中的bean同样默认为单实例!


② Prototype-原型的

容器初始化时不创建bean实例,而在每次请求时都创建一个新的bean实例,并返回。

一般会在bean中有非静态成员属性时,即bean有“状态”时,你可能会选择使用prototype。

另外,如果bean的​​scope="prototype"​​时,容器初始化bean之后将不再管理该bean的销毁。

即,关闭IOC容器,并不会调用bean的destroy()方法 !!!

Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean() 方法)时都会创建一个新的bean实例 。

根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。

Struts2中的Action,就是每次请求创建一个实例,其作用域为prototype!


③ Request

对应域对象,requestScope - 请求作用域。

在一次HTTP请求中,一个bean定义对应一个实例;
即每次HTTP请求将会有各自的bean实例,它们依据某个bean定义创建而成。
该作用域仅在基于web的Spring ApplicationContext情形下有效。

这种情况不常用,在web环境下,如果属于同一请求,直接把bean放在请求域里面了。


④ Session

对应域对象,sessionScope - 会话作用域。

在一个HTTP Session中,一个bean定义对应一个实例。
该作用域仅在基于web的Spring ApplicationContext情形下有效。


⑤ GlobalSession

在一个全局的HTTP Session中,一个bean定义对应一个实例。

典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext情形下有效。

PS:request、session以及global session 仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架)。

这些作用域仅仅在使用基于web的Spring ApplicationContext实现(如XmlWebApplicationContext)时有用。

如果在普通的Spring IoC容器中,比如像XmlBeanFactory或ClassPathXmlApplicationContext, 尝试使用这些作用域,你将会得到一个IllegalStateException异常(未知的bean作用域)。


总结如下

  • singleton:定义bean的范围为每个spring容器一个实例(默认值);
  • prototype:定义bean可以被多次实例化(使用一次就创建一次);
  • request:定义bean的范围是http请求(SpringMVC中有效);
  • session:定义 bean的范围是http会话(SpringMVC)中有效;
  • global-session:定义bean的范围是全局http会话(portlet)中有效。

【2】Bean的生命周期

这里还需要补充一下前置流程哦,比如:​​静态代码块-构造函数-实例代码块-实例成员变量​

Spring中Bean的作用域与生命周期_spring

  • ① 实例化(当程序加载beans.xml文件时),把我们的bean实例化到内存中,也可用​​factory-method​​ 来调用有参的构造器
  • ② 设置属性值,如setName
  • ③ bean实现BeanNameAware接口,则可以通过setBeanName获取id号
  • ④ bean实现BeanFactoryAware接口,则可以获取beanFactory
  • ⑤ bean实现ApplicationContextAware接口,则调用setApplicationContext
  • ⑥ bean如果和一个后置处理器关联,则会调用两个方法,见下面的程序示例,执行​​postProcessBeforeInitialization​
public Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {
System.out.println("before bean init ...."+beanName);
return bean;
}
  • ⑦ 如果Bean中有使用了@PostConstruct注解的方法,则调用该方法
  • ⑧ 如果实现InitializingBean接口,则会调用afterPropertiesSet()方法
  • ⑨ 调用定制(只有所配置的bean有,非aop)的初始化方法,xml bean里面写init-method
  • ⑩ 后置处理器的​​postProcessAfterInitialization​​方法:
public Object postProcessAfterInitialization(Object bean, String beanName)throws BeansException {
System.out.println("after bean init ...."+beanName);
return bean;
}
  • (11)使用bean
  • (12)容器关闭,
  • (13)如果存在@PreDestroy注解的方法,则调用该方法
  • (14)bean实现DisposableBean的destroy()方法关闭数据连接,socket,文件流等
  • (13)调用定制的销毁方法 xml bean里面写 destroy-method

单实例Bean默认是在容器初始化时初始化的

ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

当上面语句执行时就实例化单例bean了。如果把​​scope设成scope=”prototype”​​​ 或设置​​lazy-init=”true”,​​​则会延迟bean的实例化,bean会在​​ctx.getBean("beanName");​​语句执行时才实例化。

如果想对所有bean都应用延迟初始化,可以在根节点beans设置​​default-lazy-init=”true”​​,如下所示:

<beans default-lazy-init=”true” …>

注意: 如果bean的scope设为prototype时,当context.close时,bean的destroy方法不会被调用.

原因:对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责。容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。

不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法。但对prototype而言,任何配置好的析构生命周期回调方法都将不会 被调用。

清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被prototype作用域bean占用资源的一种可行方式是,通过使用bean的后置处理器,该处理器持有要被清除的bean的引用。)

谈及prototype作用域的bean时,在某些方面你可以将Spring容器的角色看作是Java new 操作的替代者。任何迟于该时间点的生命周期事宜都得交由客户端来处理。

参考博文:Spring中bean的初始化和销毁几种实现方式详解

【3】Bean对IOC容器的感知

容器管理的Bean一般不需要了解容器的状态和直接使用容器,但在某些情况下,是需要在Bean中直接对IOC容器进行操作的。这时候,就需要在Bean中设定对容器的感知。Spring IOC容器也提供了该功能,它是通过特定的aware接口来完成的。

aware接口有以下这些:

  • BeanNameAware,可以在Bean中得到它在IOC容器中的Bean实例名称。
  • BeanFactoryAware,可以在Bean中得到Bean所在的IOC容器,从而直接在Bean中使用IOC容器的服务。
  • ApplicationContextAware,可以在Bean中得到Bean所在的应用上下文,从而直接在Bean中使用应用上下文的服务。
  • MessageSourceAware,在Bean中可以得到消息源。
  • ApplicationEventPublisherAware,在Bean中可以得到应用上下文的事件发布器,从而可以在Bean中发布应用上下文事件。
  • ResourceLoaderAware,在Bean中可以得到ResourcesLoader,从而在Bean中使用ResourceLoader加载外部对应的Resource资源。

在设置Bean的属性之后,调用初始化回调方法之前,Spring会调用aware接口中的setter方法,以ApplicationContextAware,其只有一个方法​​setApplicationContext(ApplicationContext applicationContext) throws BeansException;​​该方法也是一个回调函数,在Bean中通过实现这个函数,可以在容器回调该aware接口方法时使注入的applicationContext引用在Bean中保存下来,供Bean需要使用ApplicationContext的基本服务时使用。这个对setApplicationContext方法的回调是由容器完成的。

可以看到一个ApplicationContextAwareProcessor 作为BeanPostProcessor的实现,对一系列的aware回调进行了调用。

class ApplicationContextAwareProcessor implements BeanPostProcessor {

private final ConfigurableApplicationContext applicationContext;

private final StringValueResolver embeddedValueResolver;


/**
* Create a new ApplicationContextAwareProcessor for the given context.
*/
public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
}


@Override
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){
return bean;
}

AccessControlContext acc = null;

if (System.getSecurityManager() != null) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}

if (acc != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareInterfaces(bean);
return null;
}, acc);
}
else {
invokeAwareInterfaces(bean);
}

return bean;
}

private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}

}

invokeAwareInterfaces方法中可以看到对EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、MessageSourceAware以及ApplicationContextAware进行了回调。

而BeanPostProcessor是在Bean生命周期中被​​AbstractAutowireCapableBeanFactory.initializeBean​​​触发的。
Spring中Bean的作用域与生命周期_生命周期_02