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

相关推荐
稻草人222221 小时前
java Excel 导出 ,如何实现八倍效率优化,以及代码分层,方法封装
后端·架构
渣哥21 小时前
原来 Java 里线程安全集合有这么多种
java
间彧21 小时前
Spring Boot集成Spring Security完整指南
java
间彧21 小时前
Spring Secutiy基本原理及工作流程
java
数据智能老司机1 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
Java水解1 天前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
数据智能老司机1 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
洛小豆1 天前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学1 天前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole1 天前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端