当前位置: 首页 > news >正文

浅浅了解下Spring中生命周期函数(Spring6全攻略)

你好,这里是codetrend专栏“Spring6全攻略”。

Spring框架设计生命周期回调函数的主要目的是为了提供一种机制,使开发人员能够在对象创建、初始化和销毁等生命周期阶段执行特定的操作。这种机制可以帮助开发人员编写更加灵活和可维护的代码。

举个例子。

缓存预热是一种在程序启动或缓存失效之后,主动将热点数据加载到缓存中的策略。

通过缓存预热能避免第一次查询数据慢的问题。

那如何在应用启动的时候把数据全量写入缓存这呢?

这个时候就可以用到Spring的生命周期函数。

在服务创建的时候写一个init函数,加上注解@PostConstruct之后,就会在应用启动的时候调用。

这样一旦数据没有在缓存,就会二次写入。

整个过程用mermaid表示如下:

应用启动
调用init函数
数据在缓存中吗?
使用缓存数据
从数据库加载数据
写入缓存

生命周期函数有哪些使用场景

Spring框架的生命周期回调函数有多种使用场景,以下是一些常见的情况:

  • 初始化资源:在Bean初始化之后,可能需要进行一些资源的初始化操作,比如建立数据库连接、加载配置信息等。通过初始化回调函数,可以在Bean准备就绪后执行这些操作。
  • 释放资源:在Bean销毁之前,可能需要进行一些资源的释放操作,比如关闭数据库连接、释放文件句柄等。通过销毁回调函数,可以在Bean即将被销毁时执行这些清理操作。
  • 依赖注入后的处理:有时候在依赖注入完成后需要执行特定的逻辑,例如根据依赖的情况进行一些动态调整或者校验。
  • 与外部系统集成:在与外部系统集成时,可能需要在Bean创建后或销毁前执行一些初始化或清理工作,例如注册到消息队列、向外部服务发送初始化请求等。
  • 日志记录:使用生命周期回调函数可以方便地记录Bean的创建、初始化和销毁等生命周期事件,以便进行调试和排查问题。
  • 定时任务:通过生命周期回调函数可以实现定时任务的启动和关闭,例如在应用启动时启动定时任务,在应用关闭时停止定时任务。

有哪些生命周期回调

默认的回调函数有如下几种:

  • 初始化回调:在Bean对象实例化后、属性注入完成之后,执行特定的初始化操作的过程。
  • 销毁回调:在Bean对象即将被销毁前执行特定的清理操作的过程。
  • 启动和停止回调:在整个Spring应用程序上下文启动和停止时执行的回调方法。

除此之外还可以通过实现接口BeanPostProcessor来完成任意的回调函数。

初始化回调

在Spring中,Bean的初始化回调可以通过实现InitializingBean接口、@PostConstruct注解或在XML配置中使用init-method来实现。下面将详细说明各种方式的用法,并举例说明。

实现InitializingBean接口

  • 实现InitializingBean接口的类需要实现afterPropertiesSet()方法,在该方法中编写初始化逻辑。
  • 示例代码如下:
@Slf4j
class MovieFinder implements InitializingBean {public List<String> findMovies() {return Arrays.asList("电影1", "电影2", "电影3");}@Overridepublic void afterPropertiesSet() throws Exception {log.info("电影数据初始化中...");}
}

使用@PostConstruct注解

  • 使用javax.annotation.PostConstruct注解标记一个方法作为初始化方法,在依赖注入完成后会自动调用该方法。
  • 把上面的代码稍微改造下,示例代码如下:
@Slf4j
class MovieFinder implements InitializingBean {public List<String> findMovies() {return Arrays.asList("电影1", "电影2", "电影3");}@Overridepublic void afterPropertiesSet() throws Exception {log.info("电影数据初始化中...");}@PostConstructpublic void init() {// 初始化逻辑log.info("电影数据初始化中...通过PostConstruct");}
}

XML配置init-method

  • 在XML配置中,可以通过init-method属性指定Bean的初始化方法,在Bean实例化后会调用该方法。
  • XML配置示例:
<bean id="myBean" class="com.example.MyBean" init-method="init">
</bean>
public class MyBean {public void init() {// 初始化逻辑System.out.println("MyBean is being initialized.");}
}

源码分析

Spring的调用链路很长,按顺序执行的方法如下:

  • AbstractAutowireCapableBeanFactory#createBean
  • AbstractAutowireCapableBeanFactory#doCreateBeanAbstractAutowireCapableBeanFactory#doCreateBean
  • AbstractAutowireCapableBeanFactory#initializeBean
  • AbstractAutowireCapableBeanFactory#invokeInitMethods

doCreateBean 调用了两个核心函数,其中第二个就是初始化函数。

// 给bean的属性设置一些逻辑
populateBean(beanName, mbd, instanceWrapper);
// 初始化逻辑,这块就是执行初始化回调的地方
exposedObject = initializeBean(beanName, exposedObject, mbd);

其中初始化的核心代码就是这段。

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)throws Throwable {// 解析实现了InitializingBean,也就是调用afterPropertiesSetboolean isInitializingBean = (bean instanceof InitializingBean);if (isInitializingBean && (mbd == null || !mbd.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) {if (logger.isTraceEnabled()) {logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");}((InitializingBean) bean).afterPropertiesSet();}// 解析各种初始化方法,自定义的、注解注入的if (mbd != null && bean.getClass() != NullBean.class) {String[] initMethodNames = mbd.getInitMethodNames();if (initMethodNames != null) {for (String initMethodName : initMethodNames) {if (StringUtils.hasLength(initMethodName) &&!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&!mbd.hasAnyExternallyManagedInitMethod(initMethodName)) {invokeCustomInitMethod(beanName, bean, mbd, initMethodName);}}}}}

销毁回调

@PreDestroy 注解

  • 功能:允许开发者通过注解标记 Bean 销毁时应执行的方法。
  • 优点:简单直观,符合 Java 标准,易于使用。
  • 使用场景:适用于需要在 Bean 销毁前执行一些清理操作,如关闭资源等。

实现 DisposableBean 接口

  • 功能:提供了一个回调接口,要求实现 destroy 方法来处理 Bean 销毁时的逻辑。
  • 优点:接口方式,强制性较强,适合需要明确销毁逻辑的场景。
  • 使用场景:适用于需要在 Bean 销毁前执行复杂操作或依赖其他 Spring Bean 的情况。

自定义销毁方法:

  • 功能:允许在配置类中指定 Bean 的销毁方法。
  • 优点:灵活性高,方法名可以自由定义。
  • 使用场景:适用于需要灵活配置的 Bean 销毁逻辑,尤其是通过 Java 配置类定义 Bean 的情况。

Bean代码如下:

/*** 服务代码*/
@Slf4j
class SimpleMovieLister implements DisposableBean {private final MovieFinder movieFinder;public SimpleMovieLister(MovieFinder movieFinder) {this.movieFinder = movieFinder;}public void listMovies() {log.info("电影列表打印中");movieFinder.findMovies().forEach(log::info);}@PreDestroypublic void onDestroy() {log.info("Bean is being destroyed");}@Overridepublic void destroy() throws Exception {log.info("DisposableBean is being destroyed");}public void customDestroy() {log.info("Custom destroy method is being called");}
}

APP配置如下:

/*** App配置*/
@Configuration
class ConstructorAppConfig{@Beanpublic MovieFinder movieFinder() {return new MovieFinder();}// destroyMethod属性能指定自定义属性@Bean(destroyMethod = "customDestroy")public SimpleMovieLister simpleMovieLister(MovieFinder movieFinder) {return new SimpleMovieLister(movieFinder);}
}

解析销毁方法需要 CommonAnnotationBeanPostProcessor,这里就在启动类手动注入了对应的处理器。想要触发还需要手动close对应的bean工厂。

/*** bean生命周期自定义* @author nine* @since 1.0*/
@Slf4j
public class BeanLifeCycleDemo {public static void main(String[] args) {// 创建一个基于 Java Config 的应用上下文AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConstructorAppConfig.class);// @Resource @PostConstruct @PreDestroycontext.registerBean(CommonAnnotationBeanPostProcessor.class);for (String beanDefinitionName : context.getBeanDefinitionNames()) {log.info("beanDefinitionName: {}", beanDefinitionName);}log.info("bean初始化完成");// 从上下文中获取名bean,其类型为PetStoreServiceSimpleMovieLister bean = context.getBean(SimpleMovieLister.class);// 调用获取的bean的方法bean.listMovies();// 销毁容器context.close();}
}

相关源码DefaultSingletonBeanRegistry#destroyBean片段如下:

/*** 销毁指定名称的 bean 及其相关处理* @param beanName 要销毁的 bean 的名称* @param bean 可销毁的 bean 对象(可能为 null)*/
protected void destroyBean(String beanName, @Nullable DisposableBean bean) {// 首先触发依赖该 bean 的其他 bean 的销毁...Set<String> dependencies;synchronized (this.dependentBeanMap) { // 在完全同步块内以确保获取到独立的集合dependencies = this.dependentBeanMap.remove(beanName);}if (dependencies!= null) {if (logger.isTraceEnabled()) {logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);}for (String dependentBeanName : dependencies) {destroySingleton(dependentBeanName); // 销毁依赖的单例}}// 现在实际销毁该 bean...if (bean!= null) {try {bean.destroy(); // 调用可销毁 bean 的销毁方法}catch (Throwable ex) {if (logger.isWarnEnabled()) {logger.warn("Destruction of bean with name '" + beanName + "' threw an exception", ex);}}}// 触发包含在该 bean 中的 bean 的销毁...Set<String> containedBeans;synchronized (this.containedBeanMap) { // 在完全同步块内以确保获取到独立的集合containedBeans = this.containedBeanMap.remove(beanName);}if (containedBeans!= null) {for (String containedBeanName : containedBeans) {destroySingleton(containedBeanName); // 销毁包含的单例}}// 从其他 bean 的依赖中移除已销毁的 bean。synchronized (this.dependentBeanMap) {for (Iterator<Map.Entry<String, Set<String>>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext();) {Map.Entry<String, Set<String>> entry = it.next();Set<String> dependenciesToClean = entry.getValue();dependenciesToClean.remove(beanName);if (dependenciesToClean.isEmpty()) {it.remove();}}}// 移除已销毁 bean 的预准备依赖信息。this.dependenciesForBeanMap.remove(beanName);
}

可以看到Spring 会在 Bean 销毁时调用 destroy 方法。

启动和关闭回调

在 Spring 框架中,Startup 和 Shutdown Callbacks 提供了在容器启动和关闭时执行特定操作的功能。

Startup Callbacks(启动回调):

  • 允许开发者在 Spring 应用程序启动时执行特定的操作,如初始化缓存、启动定时任务等。
  • 这些回调方法通常与 Bean 的初始化相关联,在容器启动后执行。

Shutdown Callbacks(关闭回调):

  • 允许开发者在 Spring 应用程序关闭时执行特定的操作,如释放资源、关闭连接等。
  • 这些回调方法通常与 Bean 的销毁相关联,在容器关闭前执行。

Spring 框架实现了这一功能通过以下几个关键点:

SmartLifecycle 接口

  • Spring 提供了 SmartLifecycle 接口,允许 Bean 实现该接口以自定义它们的启动和关闭逻辑。实现了该接口的 Bean 在容器启动和关闭时会被自动调用。

实现 SmartLifecycle 接口:

import org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;@Component
public class MyLifecycleBean implements SmartLifecycle {private boolean isRunning = false;@Overridepublic void start() {System.out.println("Bean is starting...");isRunning = true;}@Overridepublic void stop() {System.out.println("Bean is stopping...");isRunning = false;}@Overridepublic boolean isRunning() {return isRunning;}
}

在Bean工厂运行的时候就会触发对应的生命周期函数。

关于作者

来自一线全栈程序员nine的探索与实践,持续迭代中。

欢迎关注或者点个收藏~

http://www.lryc.cn/news/372132.html

相关文章:

  • 建议收藏!亚马逊卖家必须知道的37个常用术语解释
  • 黑苹果睡眠总是自动唤醒(RTC)
  • 【代码随想录训练营】【Day 49+】【动态规划-8】| Leetcode 121, 122, 123
  • k8s metrics-server服务监控pod 的 cpu、内存
  • 电脑自带录屏在哪?电脑录屏,4个详细方法
  • [Cloud Networking] Layer3 (Continue)
  • missing authentication credentials for REST request
  • Unity 从0开始编写一个技能编辑器_02_Buff系统的生命周期
  • 计算机网络简答题
  • 探索Java 8 Stream API:现代数据处理的新纪元
  • vim 删除光标到最后一行的所有内容
  • k8s之kubelet证书时间过期升级
  • 5G消息 x 文旅 | 一站式智慧文旅解决方案
  • 如何评估员工在新版FMEA培训后应用知识的效果?
  • python脚本之解析命令参数
  • 当JS遇上NLP:开启图片分析的奇幻之旅
  • trpc快速上手
  • 知识图谱存在的挑战---隐私、安全和伦理相关和测试认证相关
  • 课时155:脚本发布_简单脚本_命令罗列
  • 借助ollama实现AI绘画提示词自由,操作简单只需一个节点!
  • PyTorch -- Visdom 快速实践
  • 基于xilinx FPGA的QSFP调试使用经验
  • WPF 使用Image控件显示图片
  • 合肥工业大学内容安全实验一:爬虫|爬新闻文本
  • 自动驾驶---Perception之视觉点云雷达点云
  • maven 显式依赖包包含隐式依赖包,引起依赖包冲突
  • Spring应用如何打印access日志和out日志(用于分析请求总共在服务耗费多长时间)
  • SpringBoot整合SpringDataRedis
  • 电脑怎么录制游戏视频?轻松捕捉每一帧精彩
  • 【Elasticsearch】索引快照并还原到其他集群