Bean的作用域

一般情况下,我们书写在IOC容器中的配置信息,会在我们的IOC容器运行时被创建,这就导致我们通过IOC容器获取到bean对象的时候,往往都是获取到了单例的Bean对象。

这样就意味着无论我们使用多少个 getBean()方法,获取到的同一个 JavaBean 都是同一个对象,这就是单实例 Bean,整个项目都会共享这一个 bean 对象。

在 Spring 中,可以在元素的 scope 属性里设置 bean 的作用域,以决定这个 bean 是单实例的还是多实例的

Spring 框架支持以下五个作用域,分别为 singleton、prototype、request、session 和 global session,5种作用域说明:

  • singleton:在spring IOC容器仅存在一个Bean实例,Bean以单例方式存在,默认值
  • prototype:每次从容器中调用Bean时,都会返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()
  • request:每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境
  • session:同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境
  • global-seesion:一般用于Protlet应用环境,该作用域仅适用于WebApplicationContext环境

singleton 作用域:

singleton 是默认的作用域,也就是说,当定义 Bean 时,如果没有指定作用域配置项,则 Bean 的作用域被默认为 singleton。

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

也就是说,当将一个 bean 定义设置为 singleton 作用域的时候,Spring IoC 容器只会创建该 bean 定义的唯一实例。

Singleton 是单例类型,就是在创建起容器时就同时自动创建了一个 bean 的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton 作用域是 Spring 中的缺省作用域。你可以在 bean 的配置文件中设置作用域的属性为 singleton,如下所示:

1
2
3
4
<!-- A bean definition with singleton scope -->
<bean id="..." class="..." scope="singleton">
<!-- collaborators and configuration for this bean go here -->
</bean>

prototype 作用域

当一个 bean 的作用域为 Prototype,表示一个 bean 定义对应多个对象实例。Prototype 作用域的 bean 会导致在每次对该 bean 请求(将其注入到另一个 bean 中,或者以程序的方式调用容器的 getBean() 方法)时都会创建一个新的 bean 实例。Prototype 是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的 bean 应该使用 prototype 作用域,而对无状态的bean则应该使用 singleton 作用域。

为了定义 prototype 作用域,你可以在 bean 的配置文件中设置作用域的属性为 prototype,如下所示:

1
2
3
4
<!-- A bean definition with singleton scope -->
<bean id="..." class="..." scope="prototype">
<!-- collaborators and configuration for this bean go here -->
</bean>

Bean 的生命周期

bean的初始和销毁

在IOC中创建的每一个bean对象都是有其特定的生命周期的,在Spring的IOC容器中可以管理bean的生命周期,Spring允许在bean生命周期内待定的时间执行指定的任务。

Spring IOC容器对bean的生命周期进行管理的过程可以分为:

  • 通过构造器或工厂方法创建bean 实例
  • 为bean的属性设置值和对其他bean的引用
  • 调用bean的初始化方法
  • bean可以正常使用
  • 当容器关闭时,调用bean的销毁方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.spring.beans;


public class Book {
private String bookName;
private String author;
/**
* 初始化方法
* */
public void myInit() {
System.out.println("book bean被创建");
}

/**
* 销毁时方法
* */
public void myDestory() {
System.out.println("book bean被销毁");
}

public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "Book [bookName=" + bookName + ", author=" + author + "]";
}
}

这时我们在配置 bean 时,可以通过 init-method 和 destroy-method 属性为 bean 指定初始化和销毁方法

1
2
3
4
5
6
<!-- 设置bean的生命周期
destory-method:结束调用的方法
init-method:起始时调用的方法
-->
<bean id="book01" class="com.spring.beans.Book" destroy-method="myDestory" init-method="myInit"></bean>
复制代码

这样当我们在通过 IOC 容器创建和销毁 bean 对象时就会执行相应的方法

但是这里还是有一点需要注意

我们上面说了,单实例的 bean 和多实例的 bean 的创建时间是不同的,那么他们的初始方法和销毁方法的执行时间就稍稍有不同。

单实例下 bean 的生命周期

容器启动——>初始化方法——>(容器关闭)销毁方法

多实例下 bean 的生命周期

容器启动——>调用 bean——>初始化方法——>容器关闭(销毁方法不执行)

bean 的后置处理器

什么是 bean 的后置处理器?bean 后置处理器允许在调用初始化方法前后对 bean 进行额外的处理

bean 后置处理器对 IOC 容器里的所有 bean 实例逐一处理,而非单一实例

其典型应用是:检查 bean 属性的正确性或根据特定的标准更改 bean 的属性。

bean 后置处理器使用时需要实现接口:

org.springframework.beans.factory.config.BeanPostProcessor。

在初始化方法被调用前后,Spring 将把每个 bean 实例分别传递给上述接口的以下两个方法:

postProcessBeforeInitialization(Object, String)调用前

postProcessAfterInitialization(Object, String)调用后

如下是一个实现在该接口的后置处理器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.spring.beans;


import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;


/**
* 测试bean的后置处理器
* 在这里要注意一点是为了出现bean和beanName,而不是arg0、arg1,需要绑定相应的源码jar包
* */
public class MyBeanPostProcessor implements BeanPostProcessor{


/**
* postProcessBeforeInitialization
* 初始化方法执行前执行
* Object bean
* String beanName xml容器中定义的bean名称
* */
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
// TODO Auto-generated method stub
System.out.println("【"+ beanName+"】初始化方法执行前...");
return bean;
}


/**
* postProcessAfterInitialization
* 初始化方法执行后执行
* Object bean
* String beanName xml容器中定义的bean名称
* */
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
// TODO Auto-generated method stub
System.out.println("【"+ beanName+"】初始化方法执行后...");
return bean;
}


}
复制代码

将该后置处理器加入到 IOC 容器中:

1
2
3
<!-- 测试bean的后置处理器 -->
<bean id="beanPostProcessor" class="com.spring.beans.MyBeanPostProcessor"></bean>
复制代码

由于现在我们的 bean 对象是单实例的,所以容器运行时就会直接创建 bean 对象,同时也会执行该 bean 的后置处理器方法和初始化方法,在容器被销毁时又会执行销毁方法。我们测试如下:

1
2
3
4
5
6
7
8
9
10
//*************************bean生命周期*****************
// 由于ApplicationContext是一个顶层接口,里面没有销毁方法close,所以需要使用它的子接口进行接收
ConfigurableApplicationContext iocContext01 = new ClassPathXmlApplicationContext("ioc1.xml");

@Test
public void test01() {
iocContext01.getBean("book01");
iocContext01.close();
}
复制代码

运行结果:

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

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

总结一下后置处理器的执行过程

  • 通过构造器或工厂方法创建 bean 实例

  • 为 bean 的属性设置值和对其他 bean 的引用

  • 将 bean 实例传递给 bean 后置处理器的**postProcessBeforeInitialization()**方法

  • 调用 bean 的初始化方法

  • 将 bean 实例传递给 bean 后置处理器的**postProcessAfterInitialization()**方法

  • bean 可以使用了

  • 当容器关闭时调用 bean 的销毁方法

所以添加 bean 后置处理器后 bean 的生命周期为

容器启动——后置处理器的 before…——>初始化方法——>后置处理器的 after…———>(容器关闭)销毁方法