深入浅出spring boot2.x 学习笔记(四) Bean的生命周期和作用域

本文详细探讨了Spring Boot 2.x中Bean的生命周期,包括Bean的定义、初始化(如@PostConstruct、InitializingBean接口)、生存期和销毁(@PreDestroy、DisposableBean接口)。此外,还讲解了Bean的作用域,以及如何自定义Bean的初始化和销毁方法。对于Bean的生命周期,文章通过实例展示了初始化和销毁方法的执行顺序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Bean的生命周期

ioc容器中bean的生命周期分为四个部分:

  • Bean的定义
  • Bean的初始化
  • Bean的生存期
  • Bean销毁
  1. bean的定义过程
           容器启动时,spring通过我们的配置扫描带有@Component、@Service等注解的类并进行解析,解析的过程可以说是读取了我们对Bean(类)的描述,生成了Bean(类)的定义,并将Bean(类)的定义保存起来。然后将该定义发布到ioc容器中,但此时容器中并没有生成Bean的实例。
           bean定义的过程可以看作是扫描并发布Bean的过程。

  2. bean的初始化
           Bean的定义完成后,在默认的情况下ioc容器会进行初始化操作生成Bean的实例并完成依赖注入。
           若是想要在我们取出Bean的时候再让ioc容器对其进行初始化,则可以使用@ComponentScan中的配置项lazyInit实现懒加载。如下:

@ComponentScan(basePackages="com.example.demo.*",lazyInit = false)
public class MyConfig {

}

lazyInit属性默认为false,即不进行延迟加载。若配置为true,则该Bean在我们使用@Autowired进行注入时才进行初始化。

  • @PostConstruct注解
    该注解是由java提供的,java中的说明: @PostConstruct用来修饰一个非静态的void方法,该方法在服务器加载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的inti()方法。被@PostConstruct修饰的方法会在构造函数之后,init()方法之前运行。
    在spring中,被@PostConstruct修饰的方法在@Autowired之后立即执行,可以用来实现一些自定义的初始化。什么意思呢,看如下的代码:
public class MyTest {
	private String m;
	@Autowired
	private MyTest1 mytest1;
	
	public MyTest() {
		
	}
	@PostConstruct
	public void init() {
		System.out.println("mytest1自定义初始化操作");
	}
}

在ioc容器对MyTest进行初始化时,首先会执行MyTest的构造方法,然后变量m会被赋予初值。而mytest1依赖于依赖注入,无法在构造方法中进行初始化,而我们又需要完成某些自定义的初始化操作,则可以使用@PostConstruct注解方法来进行初始化。

  • 实现InitializingBean接口
    自定义初始化还可以通过重写InitializingBean接口的afterPropertiesSet()方法来进实现。注意:若存在@PostConstruct修饰的方法,则afterPropertiesSet()方法在其之后执行。
  1. bean的生存期
    bean的生存期即bean在程序运行时的流转。
  2. bean的销毁
  • @PreDestroy注解
    用@PreDestroy标注的方法可以在bean销毁之前进行自定义的相关操作
  • 实现DisposableBean接口,重写destory方法
    网上大部分资料中显示:在servlet生命周期中,@PreDestroy注解标注的方法会在destory()方法之后,servlet对象彻底销毁前执行。(servlet中的执行顺序我还没有进行测试)
    我们先来看一下上述两种方法在bean生命周期中的执行顺序。
@Component
public class MyTest implements DisposableBean{

	@Override
	public void destroy() throws Exception {
		System.out.println("执行destory方法");
		
	}
	@PreDestroy
	public void preDestoryTest() {
		System.out.println("preDestory方法执行");
	}
	
}

修改MyTest类,启动应用并关闭应用,控制台打印信息如下:

2019-02-14 16:27:07.379 DEBUG 14356 --- [on(2)-127.0.0.1] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown
2019-02-14 16:27:07.379 DEBUG 14356 --- [on(2)-127.0.0.1] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans
2019-02-14 16:27:07.380  INFO 14356 --- [on(2)-127.0.0.1] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
preDestory方法执行
执行destory方法
2019-02-14 16:27:07.456  INFO 14356 --- [on(2)-127.0.0.1] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2019-02-14 16:27:07.461  INFO 14356 --- [on(2)-127.0.0.1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2019-02-14 16:27:07.477  INFO 14356 --- [on(2)-127.0.0.1] 

可以看出@PreDestroy注解修饰的方法会在destory方法之前执行。

  • 使用@Bean (initMethod = ” init ”, destroyMethod = ” destroy” )来自定义bean初始化和销毁时的一些操作。首先需要一个提供自定义方法的类:
package com.example.demo.test;

public class InitAndDestory {
	public void init() {
		System.out.println("自定义init");
	}
	public void destory() {
		System.out.println("自定义destory");
	}
}

接着继续修改MyTest类:

package com.example.demo.test;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class MyTest implements DisposableBean{
	@Bean(initMethod = "init", destroyMethod = "destory")
	public InitAndDestory iAd() {
		return new InitAndDestory();
	}
	
	@PostConstruct
	public void postConstructTest() {
		System.out.println("postConstruct方法执行");
	}
	@PreDestroy
	public void preDestoryTest() {
		System.out.println("preDestory方法执行");
	}

	@Override
	public void destroy() throws Exception {
		System.out.println("这是实现DisposableBean接口的destory方法");
		
	}
	
}

启动然后关闭应用,结果如下:

2019-02-15 17:06:38.337  INFO 20508 --- [on(2)-127.0.0.1] inMXBeanRegistrar$SpringApplicationAdmin : Application shutdown requested.
2019-02-15 17:06:38.345  INFO 20508 --- [on(2)-127.0.0.1] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
自定义destory
preDestory方法执行
这是实现DisposableBean接口的destory方法
2019-02-15 17:06:38.361  INFO 20508 --- [on(2)-127.0.0.1] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2019-02-15 17:06:38.366  INFO 20508 --- [on(2)-127.0.0.1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2019-02-15 17:06:38.377  INFO 20508 --- [on(2)-127.0.0.1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

继续修改看结果:

@Component
public class MyTest{
	@Bean(initMethod = "init", destroyMethod = "destory")
	public InitAndDestory iAd() {
		return new InitAndDestory();
	}
	
	@PostConstruct
	public void postConstructTest() {
		System.out.println("postConstruct方法执行");
	}
	@PreDestroy
	public void preDestoryTest() {
		System.out.println("preDestory方法执行");
	}

}
2019-02-15 17:36:58.614  INFO 30372 --- [on(2)-127.0.0.1] inMXBeanRegistrar$SpringApplicationAdmin : Application shutdown requested.
2019-02-15 17:36:58.622  INFO 30372 --- [on(2)-127.0.0.1] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
自定义destory
preDestory方法执行
2019-02-15 17:36:58.640  INFO 30372 --- [on(2)-127.0.0.1] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2019-02-15 17:36:58.643  INFO 30372 --- [on(2)-127.0.0.1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2019-02-15 17:36:58.655  INFO 30372

再次修改看输出结果:

@Component
public class MyTest implements DisposableBean,InitializingBean{
	@Bean(initMethod = "init", destroyMethod = "destory")
	public InitAndDestory iAd() {
		return new InitAndDestory();
	}
	
	@PostConstruct
	public void postConstructTest() {
		System.out.println("postConstruct方法执行");
	}
//	@PreDestroy
//	public void preDestoryTest() {
//		System.out.println("preDestory方法执行");
//	}

	@Override
	public void destroy() throws Exception {
		System.out.println("这是实现DisposableBean接口的destory方法");
		
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		System.out.println("这是实现InitializingBean接口中的方法");
		
	}
	
}
2019-02-15 17:29:56.961  INFO 45040 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
postConstruct方法执行
这是实现InitializingBean接口中的方法
自定义init

2019-02-15 17:30:57.474  INFO 45040 --- [on(2)-127.0.0.1] inMXBeanRegistrar$SpringApplicationAdmin : Application shutdown requested.
2019-02-15 17:30:57.484  INFO 45040 --- [on(2)-127.0.0.1] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
自定义destory
这是实现DisposableBean接口的destory方法
2019-02-15 17:30:57.498  INFO 45040 --- [on(2)-127.0.0.1] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2019-02-15 17:30:57.501  INFO 45040 --- [on(2)-127.0.0.1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2019-02-15 17:30:57.510  IN

经过上述测试,bean初始化的顺序如下:

  1. bean在实例化之后,初始化之前都会执行postProcessBeforeInitialization方法,在初始化完成后执行postProcessAfterInitialization方法。
  2. 若存在@PostConstruct修饰的方法,则执行该方法。
  3. 若实现了InitializingBean接口,则执行其afterPropertiesSet方法。
  4. 若存在我们自定义的init方法,则执行自定义方法。
    bean在销毁时各个方法的顺序与网上资料中存在出入:自定义的destory方法始终在@PreDestroy修饰的销毁方法之前执行,实现DisposableBean接口重写的方法始终在最后执行(该结果与书中描述相符)。

注意:bean在初始化和销毁时,我们可以通过上述方法进行相应的自定义操作,但并不是执行了我们自定义的这些方法而完成了初始化和销毁。bean的初始化和销毁是一个过程,在过程中会相应的执行我们重写或自定义的方法。且上述测试是默认bean为单例的情况,若不是单例的bean,则初始化时仍会自动执行相关方法,但销毁时不会自动执行destory相关方法,需要由我们自己去调用。

Bean的作用域

作用域类型使用范围作用域描述
singleton所有spring应用ioc容器的默认值,只存在一个bean实例
prototype所有spring应用每从容器中取出一次bean,都是一个新的实例
session所有spring web应用http会话
application所有spring web应用web工程生命周期
request所有spring web应用web的单次请求
globalSession所有spring web应用在一个全局的httpSession中,一个bean对应一个实例

再复习一下会话(http)和请求(request)

  • 请求(request)
    http协议是无状态的,也就是说,发送一次请求得到响应之后,再次发送请求,服务器已经不知道你上一次请求所发送的任何信息。
  • 会话(session)
    session会话会占用服务器的一段内存空间保存多个有联系request请求之间需要保存的共享变量,大部分session的实现对同一客户的 相同 浏览器的 一段时间 (即session超时时间)内的请求作状态共享和保持,不同浏览器之间的session不共享。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值