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方法的回调。

相关推荐
stein_java1 小时前
springMVC-10验证及国际化
java·spring
weixin_478689761 小时前
C++ 对 C 的兼容性
java·c语言·c++
打码人的日常分享1 小时前
智慧城市建设方案
大数据·架构·智慧城市·制造
LUCIAZZZ1 小时前
HikariCP数据库连接池原理解析
java·jvm·数据库·spring·springboot·线程池·连接池
sky_ph2 小时前
JAVA-GC浅析(二)G1(Garbage First)回收器
java·后端
鲁班大叔_0072 小时前
使用扣子与Dify的业务风险
架构·产品
IDRSolutions_CN2 小时前
PDF 转 HTML5 —— HTML5 填充图形不支持 Even-Odd 奇偶规则?(第二部分)
java·经验分享·pdf·软件工程·团队开发
hello早上好2 小时前
Spring不同类型的ApplicationContext的创建方式
java·后端·架构
Python智慧行囊3 小时前
Python 中 Django 中间件:原理、方法与实战应用
python·中间件·架构·django·开发
HelloWord~3 小时前
SpringSecurity+vue通用权限系统2
java·vue.js