您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

一文理解SpringBean生命周期之PostConstruct、PreDestroy详解

时间:2022-06-21 15:09:53  来源:掘金  作者:JAVA旭阳

概述

想必大家在项目中都用过@PostConstruct这个注解把,知道它会在应用启动的时候执行被这个注解标注的方法。其实它还有另外一个注解@PreDestroy,实在Bean销毁前执行,它们都是Bean生命周期的一环,那他们具体在什么阶段执行呢?我们从源码的角度带大家分析下。

注解介绍

@PostConstruct和@PreDestroy是JSR-250规范中定义的类两个类,表示Bean初始化后和销毁前指定的注解,位于JAVAx.annotation包下,而不是spring jar中的类。

JSR-250, Java Specification Requests的缩写,意思是Java 规范提案。它是Java界共同制定的一个重要标准。它定义了一组通用的注解,比如@PostContruct, @Resource等,防止不同的J2EE组件比如Spring、JBoss、WebSphere等都各自实现一套注解。

Spring作为一个NB的框架,它也遵循上面的规范,实现了对JSR注解的支持。

@PostConstruct

@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PostConstruct {
}
  • 该注解只能作用于方法上,执行依赖注入后执行任何初始化操作。必须在类投入服务之前调用此方法。
  • 应用PostConstruct的方法可以是公共的、受保护的、包私有的或私有的,但不能是静态的。
  • 被注解方法不能有任何参数。

@PreDestroy

@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PreDestroy {
}
  • 作用于方法上,在容器销毁Bean的时候回调执行。
  • 被注解方法不能有任何参数。
  • 应用PreDestroy的方法可以是公共的、受保护的、包私有的或私有的,但不能是静态的。

实战案例

  1. 定义bean
@Slf4j
@ToString
public class LifeCycleBean implements InitializingBean {

    private String prop;

    public LifeCycleBean() {
        log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean 实例化");
    }

    public LifeCycleBean(String prop) {
        this.prop = prop;
    }

    public String getProp() {
        return prop;
    }


    @PostConstruct
    private void postContruct() {
        log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean postContruct");
    }

    @PreDestroy
    private void preDestory() {
        log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean preDestory");
    }
    

    public void setProp(String prop) {
        this.prop = prop;
    }


    public void init() {
        log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean 初始化");
        this.setProp("hello");
    }

    public void destroy() {
        log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean destroy");
        this.setProp("hello");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("%%%%%%%%%%%%%%%%%%%% LifeCycleBean afterPropertiesSet");
    }
}
    @Bean(name = "lifeCycleBean", initMethod = "init", destroyMethod = "destroy")
    public LifeCycleBean createLifeCycleBean() {
        return new LifeCycleBean();
    };

定义了Bean初始化和销毁相关的方法,包括实现了InitializingBean接口,Bean配置了initMethod、destroyMethod属性,以及添加了@PostConstruct、@PreDestroy注解。

  1. 查看执行结果
一文理解SpringBean生命周期之PostConstruct、PreDestroy详解

 

代码地址:Github.com/alvinlkk/sp…

  1. 小结

根据执行结果得知bean初始化和销毁的顺序:

  1. @PostContruct注解对应的方法
  2. 实现了InitializingBean接口的afterPropertiesSet方法
  3. Beaninit-method属性对应的方法
  4. @PreDestroy注解对应的方法
  5. Beandestroy-method属性对应的方法

源码解析

一文理解SpringBean生命周期之PostConstruct、PreDestroy详解

 

通过debug快速追踪到实在Bean的初始化阶段。

  1. AbstractAutowireCapableBeanFactory的initializeBean()方法是bean的初始化入口。
  2. InitDestroyAnnotationBeanPostProcessorBean处理器中调用invokeInitMethods执行@PostContruct对应的方法。

执行过程

一文理解SpringBean生命周期之PostConstruct、PreDestroy详解

 


一文理解SpringBean生命周期之PostConstruct、PreDestroy详解

 

在Bean的初始化过程前,会回调BeanPostProcessor的
postProcessBeforeInitialization方法,这是Spring的一个扩展点,而我们的@PostConstruct就是通过这种扩展机制实现的,它对应的类是InitDestroyAnnotationBeanPostProcessor。


InitDestroyAnnotationBeanPostProcessor,顾名思义,它是用来处理初始化和销毁注解的一个Bean处理器,我们看下它的postProcessBeforeInitialization方法。

一文理解SpringBean生命周期之PostConstruct、PreDestroy详解

 

这个方法关键是上面的两步,第一步找出所有注解的方法,第二步执行对应的方法,第二步比较简单,就是调用了反射操作,我们重点关注在第一步findLifecycleMetadata方法。

/**
 * 查找指定类型的生命周期元数据.
 */
private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
    // lifecycleMetadataCache为空,则重新创建生命周期元数据.
		if (this.lifecycleMetadataCache == null) {
			// HAppens after deserialization, during destruction...
			return buildLifecycleMetadata(clazz);
		}
        // 采用双重检查锁机制来进行快速检查,尽量减少对锁的使用。
        // 首先进行快速检查,只需最少的锁竞争.
		// Quick check on the concurrent map first, with minimal locking.
		LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
		if (metadata == null) {
            // 加锁处理
			synchronized (this.lifecycleMetadataCache) {
				metadata = this.lifecycleMetadataCache.get(clazz);
				if (metadata == null) {
                    // 关键方法,构建LifecycleMetadata
					metadata = buildLifecycleMetadata(clazz);
					this.lifecycleMetadataCache.put(clazz, metadata);
				}
				return metadata;
			}
		}
		return metadata;
	}

查看关键的方法buildLifecycleMetadata的源码如下:

/**
 * 创建生命周期元数据.
 */
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
		// 判断当前Bean是否包含@PostContruct,@PreDestroy,如果不包含,直接返回
       if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {
			return this.emptyLifecycleMetadata;
		}
        // 初始化相关元数据
		List<LifecycleElement> initMethods = new ArrayList<>();
        // 销毁相关的元数据
		List<LifecycleElement> destroyMethods = new ArrayList<>();
		Class<?> targetClass = clazz;

		do {
			final List<LifecycleElement> currInitMethods = new ArrayList<>();
			final List<LifecycleElement> currDestroyMethods = new ArrayList<>();
            // 遍历类中的methods
			ReflectionUtils.doWithLocalMethods(targetClass, method -> {
                // 判断方法是有@PostConstruct注解
				if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
					LifecycleElement element = new LifecycleElement(method);
					currInitMethods.add(element);
					if (logger.isTraceEnabled()) {
						logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
					}
				}
                 // 判断方法是有@PreDestroy注解
				if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
					currDestroyMethods.add(new LifecycleElement(method));
					if (logger.isTraceEnabled()) {
						logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
					}
				}
			});

			initMethods.addAll(0, currInitMethods);
			destroyMethods.addAll(currDestroyMethods);
            // 查找父类
			targetClass = targetClass.getSuperclass();
		}
		while (targetClass != null && targetClass != Object.class);

		return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
				new LifecycleMetadata(clazz, initMethods, destroyMethods));
	}
复制代码

该方法主要是遍历bean对应的class以及父class中包含@PostConstruct、@PreDestroy注解的方法,构建出

LifecycleMetadata对象。

有一个问题,上面查找其实用的都是initAnnotationType、@PostConstruct、@PreDestroy属性,那他们是在上面时候设置为@PostConstruct、@PreDestroy呢?

我们可以看父类
CommonAnnotationBeanPostProcessor的构造方法,它在实例化CommonAnnotationBeanPostProcessor这个bean的时候设置。

一文理解SpringBean生命周期之PostConstruct、PreDestroy详解

 

public CommonAnnotationBeanPostProcessor() {
		setOrder(Ordered.LOWEST_PRECEDENCE - 3);
        // 设置PostConstruct
		setInitAnnotationType(PostConstruct.class);
		setDestroyAnnotationType(PreDestroy.class);
		ignoreResourceType("javax.xml.ws.WebServiceContext");

		// java.naming module present on JDK 9+?
		if (jndiPresent) {
			this.jndiFactory = new SimpleJndiBeanFactory();
		}
	}

何时实例化CommonAnnotationBeanPostProcessor

那么
CommonAnnotationBeanPostProcessor这个Bean处理器是在什么时候装载到容器中呢?只有它装载到容器中后,才会执行对应的方法。

一文理解SpringBean生命周期之PostConstruct、PreDestroy详解

 

  1. 在创建容器的时候,会创建所有的BeanPostProcessors Bean。
  2. 在创建CommonAnnotationBeanPostProcessor这个Bean的时候,就会调用对应的构造方法,设置对应的PostConstruct注解。

大家可以看下这篇文章了解BeanPostProcessor的详细过程SpringBoot扩展点——一文掌握BeanPostProcessor家族

那又是什么时候把
CommonAnnotationBeanPostProcessor这个Bean的BeanDefinition加到BeanDefinition工厂中的呢?

只有BeanDefinition工厂中又对应的BeanDefinition才会创建出Bean。答案就是在AnnotationConfigUtils#
registerAnnotationConfigProcessors方法中。

一文理解SpringBean生命周期之PostConstruct、PreDestroy详解

 

总结

一文理解SpringBean生命周期之PostConstruct、PreDestroy详解

 

上面时Bean初始化完整的过程,其实Bean初始化可以自定义的扩展点很多,大家可以根据实际需要扩展。


作者:JAVA旭阳
链接:
https://juejin.cn/post/7111258070352658446



Tags:SpringBean   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
一文理解SpringBean生命周期之PostConstruct、PreDestroy详解
概述想必大家在项目中都用过@PostConstruct这个注解把,知道它会在应用启动的时候执行被这个注解标注的方法。其实它还有另外一个注解@PreDestroy,实在Bean销毁前执行,它们都是B...【详细内容】
2022-06-21  Search: SpringBean  点击:(574)  评论:(0)  加入收藏
▌简易百科推荐
Qt与Flutter:在跨平台UI框架中哪个更受欢迎?
在跨平台UI框架领域,Qt和Flutter是两个备受瞩目的选择。它们各自具有独特的优势,也各自有着广泛的应用场景。本文将对Qt和Flutter进行详细的比较,以探讨在跨平台UI框架中哪个更...【详细内容】
2024-04-12  刘长伟    Tags:UI框架   点击:(7)  评论:(0)  加入收藏
Web Components实践:如何搭建一个框架无关的AI组件库
一、让人又爱又恨的Web ComponentsWeb Components是一种用于构建可重用的Web元素的技术。它允许开发者创建自定义的HTML元素,这些元素可以在不同的Web应用程序中重复使用,并且...【详细内容】
2024-04-03  京东云开发者    Tags:Web Components   点击:(11)  评论:(0)  加入收藏
Kubernetes 集群 CPU 使用率只有 13% :这下大家该知道如何省钱了
作者 | THE STACK译者 | 刘雅梦策划 | Tina根据 CAST AI 对 4000 个 Kubernetes 集群的分析,Kubernetes 集群通常只使用 13% 的 CPU 和平均 20% 的内存,这表明存在严重的过度...【详细内容】
2024-03-08  InfoQ    Tags:Kubernetes   点击:(23)  评论:(0)  加入收藏
Spring Security:保障应用安全的利器
SpringSecurity作为一个功能强大的安全框架,为Java应用程序提供了全面的安全保障,包括认证、授权、防护和集成等方面。本文将介绍SpringSecurity在这些方面的特性和优势,以及它...【详细内容】
2024-02-27  风舞凋零叶    Tags:Spring Security   点击:(61)  评论:(0)  加入收藏
五大跨平台桌面应用开发框架:Electron、Tauri、Flutter等
一、什么是跨平台桌面应用开发框架跨平台桌面应用开发框架是一种工具或框架,它允许开发者使用一种统一的代码库或语言来创建能够在多个操作系统上运行的桌面应用程序。传统上...【详细内容】
2024-02-26  贝格前端工场    Tags:框架   点击:(51)  评论:(0)  加入收藏
Spring Security权限控制框架使用指南
在常用的后台管理系统中,通常都会有访问权限控制的需求,用于限制不同人员对于接口的访问能力,如果用户不具备指定的权限,则不能访问某些接口。本文将用 waynboot-mall 项目举例...【详细内容】
2024-02-19  程序员wayn  微信公众号  Tags:Spring   点击:(41)  评论:(0)  加入收藏
开发者的Kubernetes懒人指南
你可以将本文作为开发者快速了解 Kubernetes 的指南。从基础知识到更高级的主题,如 Helm Chart,以及所有这些如何影响你作为开发者。译自Kubernetes for Lazy Developers。作...【详细内容】
2024-02-01  云云众生s  微信公众号  Tags:Kubernetes   点击:(58)  评论:(0)  加入收藏
链世界:一种简单而有效的人类行为Agent模型强化学习框架
强化学习是一种机器学习的方法,它通过让智能体(Agent)与环境交互,从而学习如何选择最优的行动来最大化累积的奖励。强化学习在许多领域都有广泛的应用,例如游戏、机器人、自动驾...【详细内容】
2024-01-30  大噬元兽  微信公众号  Tags:框架   点击:(71)  评论:(0)  加入收藏
Spring实现Kafka重试Topic,真的太香了
概述Kafka的强大功能之一是每个分区都有一个Consumer的偏移值。该偏移值是消费者将读取的下一条消息的值。可以自动或手动增加该值。如果我们由于错误而无法处理消息并想重...【详细内容】
2024-01-26  HELLO程序员  微信公众号  Tags:Spring   点击:(94)  评论:(0)  加入收藏
SpringBoot如何实现缓存预热?
缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制。那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到缓存系...【详细内容】
2024-01-19   Java中文社群  微信公众号  Tags:SpringBoot   点击:(91)  评论:(0)  加入收藏
相关文章
    无相关信息
站内最新
站内热门
站内头条