Spring中Bean初始化与销毁实现

本文中源码来自Spring 5.3.x分支,github源码地址:github.com/spring-proj...

一 示例

Java 复制代码
package com.xiakexing.service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.stereotype.Component;

@Component
public class UserService {

    public void test() {
        System.out.println("创建了UserService");
    }

    @PostConstruct
    public void afterConstruct() {
        System.out.println("UserService PostConstruct");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("UserService destroy");
    }
}
Java 复制代码
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean("userService", UserService.class);
userService.test();
context.close();

@PostConstruct和@PreDestroy注解的方法,是如何被调用的呢?

二 @PostConstruct实现

在创建Bean的初始化前阶段,将执行BeanPostProcessor.postProcessBeforeInitialization()。在InitDestroyAnnotationBeanPostProcessor实现类,完成了对@PostConstruct的查找和回调。

2.1 InitDestroyAnnotationBeanPostProcessor

来看类注释

Allows for an annotation alternative to Spring's org.springframework.beans.factory.InitializingBean and org.springframework.beans.factory.DisposableBean callback interfaces.

允许一个注释替代Spring的org.springframework.beans.factory.InitializingBean和org.springframework.beans.factory.DisposableBean回调接口。

Init and destroy annotations may be applied to methods of any visibility: public, package-protected, protected, or private. Multiple such methods may be annotated, but it is recommended to only annotate one single init method and destroy method, respectively.

Init和destroy注解可以应用于任何可见性的方法:public、package-protected、protected或private。可以注释多个这样的方法,但建议分别只注释一个init方法和destroy方法。

有以下结论:

  1. @PostConstruct是为代替InitializingBean接口,@PreDestroy是为代替DisposableBean接口;
  2. 这两个注解可修饰带有任何权限修饰符的方法;
  3. 在一个类中,可以多次使用这两个注解;但建议只注释一个init方法和destroy方法。

2.2 收集生命周期方法

首先在class中查找init和destory方法,封装为LifecycleElement对象,为每个Class创建一个LifecycleMetadata对象。 LifecycleMetadata一经构建,就放入lifecycleMetadataCache中缓存。

initAnnotationType其实就是PostConstruct.class,destroyAnnotationType就是PreDestroy.class。这两属性是在子类CommonAnnotationBeanPostProcessor中赋值的。

ReflectionUtils#doWithLocalMethods中,会遍历类中每个方法,执行MethodCallback.doWith。(函数式接口,类似于Consumer)

2.3 执行初始化方法

遍历initMethods,反射调用。

三 Bean的销毁

关闭Spring容器,通常有两种方式:

  1. 主动调用ApplicationContext.close();
  2. 或者ConfigurableApplicationContext.registerShutdownHook(),注册钩子,在进程结束时将关闭Spring容器。

在Spring容器将关闭前,想要对bean执行自定义的销毁动作。通常有以下实现方式:

  1. 实现DisposableBean接口,复写destroy();
  2. 实现AutoCloseable接口,复写close();
  3. BeanDefinition中指定了destroyMethod,如通过@Bean(destroyMethod="XXX");
  4. 使用@PreDestroy标注bean的销毁方法。而该注解就是为了代替DisposableBean接口。

在Bean被创建的最后一步,会将定义了销毁逻辑的Bean,注册到disposableBeans缓存中。

3.1 适配器模式

如果一个Bean实现了DisposableBean接口,或者实现了AutoCloseable接口,或者在BeanDefinition中指定了destroyMethod,那么这个Bean都属于"DisposableBean",在容器关闭时都要调用相应的销毁方法。

为了将各个销毁方式,统一适配成DisposableBean实现,就用到DisposableBeanAdapter。

3.2 Bean的销毁

关闭Spring容器时,会调用AbstractApplicationContext#doClose,有如下流程:

  1. 首先发布ContextClosedEvent事件;
  2. 调用lifecycleProcessor的onCloese()方法;
  3. 销毁单例Bean;
  4. 调用closeBeanFactory,关闭容器;
  5. 执行AbstractApplicationContext#onClose模板方法;

可见,此时会触发对单例Bean的销毁。在DefaultSingletonBeanRegistry#destroySingletons中,将遍历disposableBeans缓存:

  1. 把每个disposableBean从singletonObjects、singletonFactories中移除
  2. 如果这个disposableBean被其他Bean依赖了,那么先销毁依赖Bean
  3. 调用disposableBean的destroy(),销毁当前disposableBean;
  4. 如果当前disposableBean还包含了inner beans,将这些Bean也销毁;
  5. 清空各种缓存Map,包括singletonObjects、dependentBeanMap等。

3.3 @PreDestroy实现

DestructionAwareBeanPostProcessor接口,在Bean对象销毁之前,可执行自定义逻辑。

  • postProcessBeforeDestruction:Bean销毁前的自定义动作;
  • requiresDestruction:确定给定的bean实例是否需要执行这个后处理器,默认返回true。

当容器中有DestructionAwareBeanPostProcessor对象,且requiresDestruction方法对某个Bean返回true时,这个Bean也会被注册到disposableBeans缓存中。

当容器关闭时,对某个Bean执行DisposableBean.destroy()前,会先执行DestructionAwareBeanPostProcessor.postProcessBeforeDestruction方法。

源码中有个默认实现InitDestroyAnnotationBeanPostProcessor,完成了对@PreDestroy方法的回调。

相关推荐
zww89491111 小时前
匿名树洞系统开发实战:从需求分析到部署指南
java·eclipse
黑马程序员毕设2 小时前
基于Java的医院药品管理系统的优化设计与实现
java·开发语言·spring boot·小程序·架构·课程设计·毕设
头茬韭菜2 小时前
第 01 篇:「架构鸟瞰与进程启动链路」—— JobManager / TaskManager 从零长出来的完整调用链
架构·flink
CaseyWei3 小时前
Harness 架构 Multi‑Agent(多智能体)完整深度解析
人工智能·ai·架构·harness
木井巳3 小时前
【BFS/DFS 解决 FloodFill 算法】太平洋大西洋水流问题
java·算法·leetcode·深度优先·广度优先·宽度优先·推荐算法
白山编程大哥3 小时前
Java 集合算法:从排序、查找到底层原理的实战指南
java·python·算法
devpotato3 小时前
缓存与数据库更新顺序不一致问题
java·数据库·redis
xcl09253 小时前
酒馆预约系统开发实战:从需求分析到上线全流程指南
java·spring boot·需求分析
Kyrie_kk4 小时前
Java--IO--Path文件访问
java·后端
qinqinzqq4 小时前
Maven POM 格式、Schema 与 XSD:一篇讲透
java·maven