一文带你深入理解SpringBean生命周期之PostConstruct、PreDestroy详解

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

爱是一种力量,让我们超越时空感知它的存在。

概述

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

注解介绍

@PostConstruct@PreDestroyJSR-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配置了initMethoddestroyMethod属性,以及添加了@PostConstruct@PreDestroy注解。

  1. 查看执行结果

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

  1. 小结

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

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

源码解析

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

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

执行过程

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

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

这个方法关键是上面的两步,第一步找出所有注解的方法,第二步执行对应的方法,第二步比较简单,就是调用了反射操作,我们重点关注在第一步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的时候设置。

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处理器是在什么时候装载到容器中呢?只有它装载到容器中后,才会执行对应的方法。

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

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

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

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

总结

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

参考

securitit.blog.csdn.net/article/det…

blog.csdn.net/securitit/a…

www.cnblogs.com/lay2017/p/1…

猜你喜欢

转载自juejin.im/post/7111258070352658446