Bean 的作用域指的是 Bean 在 Spring 容器中的行为(Bean 实例创建及生命周期),它的行为是由 Spring 来管理的,可以根据具体情况选择不同的作用域来达到性能优化、资源利用最大化和可维护性等目的。
Bean 作用域(Scope)类型主要有如下几种:
其中前两种是 Spring 核心作用域,而后 4 种是 Spring MVC 中的作⽤域;
这里就简单用singleton
和prototype
这两种可以在 Spring 核心代码中使用的模式来演示区别一下。
package com.tr.demo.model;
public class User {
private int id;
private String name;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
一般有两种方式设置 Spring Bean 作用域:
xml
配置的方式,我们将bean
标签当中的scope
属性设置为singleton
就为单例,设置为prototype
就为多例模式。@Scope
注解,不设置参数或设置参数为singleton
就为单例,设置为prototype
就为多例模式。我们来看一下 singleton 和 prototype 两种模式下获取的 Bean 有什么不同,我们这里演示在这两种不同的模式下将 User 对象放到 Spring 容器中,从容器中先后两次取出 User,看这两次取出的 Bean 是否相同。
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
User user1 = context.getBean("user", User.class);
User user2 = context.getBean("user",User.class);
System.out.println(user1 == user2);
}
singleton 模式下的结果:
可以看到,此时 User 类在 Ioc 容器中只创建了一个实例,而通过 applicationContext.getBean() 方法多次获取到的 Bean,都是同一个对象实例,相当于是一个浅拷贝。
这里之所以这里打印出来的 Bean 是 2 个而不是 3 个,是因为prototype模式下在我们创建容器时并不会进行 Bean 的实例化,它在我们获取 Bean 的时候才会去创建 1 个实例,而且每次获取 Bean 时都会创建新的实例,它们彼此之间都是不同的实例,相当于是一个深拷贝。
Spring 的生命周期大致走的的是如下流程
小结,Bean执行流程(Spring执行流程):启动 Spring 容器 -> 实例化 Bean(分配内存空间,从无到有)-> Bean 注册到 Spring 容器中(存操作) -> 将 Bean 装配到需要的类中(取操作)。
Bean 的生命周期,就是 Bean 对象从诞生到销毁的整个过程,Bean 的生命周期大致有 5 个阶段:
①Bean 的实例化(分配内存空间)。
②设置 Bean 属性(进行依赖注入,将依赖的 Bean 辅助到当前类的属性上)。
③Bean 的初始化阶段:
④使用 Bean。
⑤销毁 Bean,执行 销毁容器的各种⽅法,如 @PreDestroy、DisposableBean 接⼝⽅法、destroy-method。
要注意设置属性必须在初始化之前,因为有可能在初始化的时候使用Bean。
上述流程可以以⽣活中的场景来理解它,⽐如我们现在需要买⼀栋房⼦,那么我们的流程是这样的:
执行流程如下图所示:
还需要注意区分一下实例化和初始化,实例化和属性设置是 Java 级别的系统“事件”,其操作过程不可⼈⼯⼲预和修改;⽽初始化是给开发者 提供的,可以在实例化之后,类加载完成之前进⾏⾃定义“事件”处理。
下面就简单的写几个周期方法来演示一下这个过程,在类中添加两个方法,一个是init
方法表示初始化,另外一个是destroy
方法,表示Bean销毁前执行的事情,使用@PostConstruct
注解或者xml
的bean
标签中的init-method
属性表示在构造后执行,@PreDestroy
注解或者xml
的bean
标签中的destroy-method
表示在 Bean 销毁前做的事情。
import com.tr.demo.model.User;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class BeanLifeComponent implements BeanNameAware {
@Autowired
private User user;
@Override
public void setBeanName(String s) {
System.out.println("执行了 BeanNameAware 通知 -> " + s);
}
@PostConstruct
public void doPostConstruct() {
System.out.println("执行了 @PostConstruct (注解式初始化)");
System.out.println(user.toString());
}
public void myInit(){
System.out.println("执行了 myinit (xml 式初始化)");
}
@PreDestroy
public void doPreDestroy(){
System.out.println("执行了 @PreDestroy (销毁 Bean 前执行)");
}
public void myDestroy() {
System.out.println("执行了 myDestroy (销毁 Bean 前执行)");
}
public void sayHi(){
System.out.println("使用 Bean");
}
}
Spring 配置文件,将 Bean 手动存储在 Spring 容器中,要注意init-method
,destroy-method
这两个属性的值要和上面实现的方法名对应。
<bean id="mybean" class="BeanLifeComponent"
init-method="myInit" destroy-method="myDestroy">
</bean>
测试代码:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanLifeTest {
public static void main(String[] args) {
// 父类 ApplicationContext 中没有 close, destroy 系列方法
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
BeanLifeComponent component =
context.getBean("mybean",BeanLifeComponent.class);
component.sayHi();
// 关闭容器
context.close();
}
}
运行结果:
如果代码中飘红说 @PostConstruct 和 @PreDestroy 注解如果找不到,需要导入下面的jar包。
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version></dependency>
找不到的原因是,从 JDK9 以后 jdk 中的 javax.annotation 包被移除了,这两个注解刚好就在这个包中。
@Scope
名称 | @Scope |
---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 设置该类创建对象的作用范围 可用于设置创建出的bean是否为单例对象 |
属性 | value(默认):定义bean作用范围, 默认值singleton(单例),可选值prototype(非单例) |
@PostConstruct
名称 | @PostConstruct |
---|---|
类型 | 方法注解 |
位置 | 方法上 |
作用 | 设置该方法为初始化方法 |
属性 | 无 |
@PreDestroy
名称 | @PreDestroy |
---|---|
类型 | 方法注解 |
位置 | 方法上 |
作用 | 设置该方法为销毁方法 |
属性 | 无 |
注解功能与 XML 实现对应关系: