Spring 中 Bean 的作用域和生命周期?

Java 中的公共类称之为 Bean 或 Java Bean,而 Spring 中的 Bean 指的是将对象的生命周期,交给 Spring IoC 容器来管理的对象。所以 Spring 中的 Bean 对象在使用时,无需通过 new 来创建对象,只需要通过 DI(依赖注入),从 Spring 中取出要使用的对象即可。 那么 Spring 中,Bean 的生命周期又有哪些呢?接下来,我们一起来看。

1.通过⼀个案例来看 Bean 作⽤域的问题

假设现在有⼀个公共的 Bean,提供给 A ⽤户和 B ⽤户使⽤,然⽽在使⽤的途中 A ⽤户却“悄悄”地修 改了公共 Bean 的数据,导致 B ⽤户在使⽤时发⽣了预期之外的逻辑错误。(说好⼀起到⽩头,你却悄 悄焗了油 )。
我们预期的结果是,公共 Bean 可以在各⾃的类中被修改,但不能影响到其他类。
1.1作用域

Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式。比如 singleton 单例作用域,就表示 Bean 在整个 Spring 中只有一份,它是全局共享的,当有人修改了这个值之后,那么另一个人读取到的就是被修改后的值。 举个例子,比如我们在 Spring 中定义了一个单例的 Bean 对象 user(默认作用域为单例),具体实现代码如下:

然后,在 A 类中使用并修改了 user 对象,具体实现代码如下:

最后,在 B 类中也使用了 user 对象,具体实现代码如下:

此时我们访问 B 对象中的 getUser 方法,就会发现此时的用户名为 A 类中修改的“李四”,而非原来的“张三”,这就说明 Bean 对象 user 默认就是单例的作用域。如果有任何地方修改了这个单例对象,那么其他类再调用就会得到一个修改后的值。

操作以上问题的原因是因为 Bean 默认情况下是单例状态(singleton),也就是所有⼈的使⽤的都是同 ⼀个对象,之前我们学单例模式的时候都知道,使⽤单例可以很⼤程度上提⾼性能,所以在 Spring 中 Bean 的作⽤域默认也是 singleton 单例模式。

2.作用域分类

在 Spring 中,Bean 的常见作用域有以下 5 种:

  1. singleton:单例作用域;
  2. prototype:原型作用域(多例作用域);
  3. request:请求作用域;
  4. session:会话作用域;
  5. application:全局作用域。

注意:后 3 种作用域,只适用于 Spring MVC 框架。

2.1 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 默认选择该作用域。

2.2 prototype

  • 官方说明:Scopes a single bean definition to any number of object instances.

  • 描述:每次对该作用域下的 Bean 的请求都会创建新的实例:获取 Bean(即通过 applicationContext.getBean 等方法获取)及装配 Bean(即通过 @Autowired 注入)都是新的对象实例。

  • 场景:通常有状态的 Bean 使用该作用域。

2.3 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 框架中使用。

2.4 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 框架中使用。

2.5 application

  • 官方说明:Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.

  • 描述:在一个 Http Servlet Context 中,定义一个 Bean 实例。

  • 场景:Web 应用的上下文信息,比如:记录一个应用的共享信息。

  • 备注:限定 Spring MVC 框架中使用。

3.作用域设置

  1. 直接设置作用域的具体值,如:@Scope("prototype");
  2. 设置 ConfigurableBeanFactory 和 WebApplicationContext 提供的 SCOPE_XXX 变量,如 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)。

具体设置代码如下:

然后我们再来看看运行结果:

4.Spring 执⾏流程

Bean 执⾏流程(Spring 执⾏流程):启动 Spring 容器 -> 实例化 Bean(分配内存空间,从⽆到
有) -> Bean 注册到 Spring 中(存操作) -> 将 Bean 装配到需要的类中(取操作)。

5.Bean 生命周期

Spring 中 Bean 的生命周期是指:Bean 在 Spring(IoC)中从创建到销毁的整个过程。 Spring 中 Bean 的生命周期主要包含以下 5 部分:

  1. 实例化:为 Bean 分配内存空间;
  2. 设置属性:将当前类依赖的 Bean 属性,进行注入和装配;
  3. 初始化:
    1. 执行各种通知;
    2. 执行初始化的前置方法;
    3. 执行初始化方法;
    4. 执行初始化的后置方法。
  4. 使用 Bean:在程序中使用 Bean 对象;
  5. 销毁 Bean:将 Bean 对象进行销毁操作。

以上生命周期中,需要注意的是:“实例化”和“初始化”是两个完全不同的过程,千万不要搞混,实例化只是给 Bean 分配了内存空间,而初始化则是将程序的执行权,从系统级别转换到用户级别,并开始执行用户添加的业务代码

5.1 代码演示

最后,在 Spring 的启动类中获取 Bean,具体实现代码如下

从上面的执行结果可以看出,代码执行顺序符合 Bean 生命周期的执行顺序:

  • 实例化:为 Bean 分配内存空间;
  • 设置属性:将当前类依赖的 Bean 属性,进行注入和装配;
  • 初始化:
    1. 执行各种通知;
    2. 执行初始化的前置方法;
    3. 执行初始化方法;
    4. 执行初始化的后置方法。
  • 使用 Bean:在程序中使用 Bean 对象;
  • 销毁 Bean:将 Bean 对象进行销毁操作。

那么问题来了,能不能先执行初始化再执行设置属性呢?也就是将生命周期中的步骤 2 和步骤 3 的执行顺序交换一下? 答案是否定的。看以下代码:

此时如果先执行步骤 2,先将 UserService 注入到当前类,再调用步骤 3 执行初始化,那么程序的执行是正常的。然而如果将交互步骤 2 和步骤 3 的执行顺序,那么程序执行就会报错(空指针异常),所以 Bean 的生命周期的顺序必须是:

1.实例化:为 Bean 分配内存空间; 2.设置属性:将当前类依赖的 Bean 属性,进行注入和装配; 3.初始化:

  1. 执行各种通知;
  2. 执行初始化的前置方法;
  3. 执行初始化方法;
  4. 执行初始化的后置方法。 4.使用 Bean:在程序中使用 Bean 对象; 5.销毁 Bean:将 Bean 对象进行销毁操作。

总结

Bean 的生命周期指的是 Bean 在 Spring(IoC)中从创建到销毁的整个过程。Bean 的生命周期主要包含以下 5 个流程: 1.实例化:为 Bean 分配内存空间; 2.设置属性:将当前类依赖的 Bean 属性,进行注入和装配; 3.初始化:

  1. 执行各种通知;
  2. 执行初始化的前置方法;
  3. 执行初始化方法;
  4. 执行初始化的后置方法。 4.使用 Bean:在程序中使用 Bean 对象; 5.销毁 Bean:将 Bean 对象进行销毁操作。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值