spring 声明周期钩子方法

Posted QQ_851228082

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring 声明周期钩子方法相关的知识,希望对你有一定的参考价值。

spring bean生命周期钩子方法执行顺序

spring bean生命周期钩子方法一共有3种,执行顺序如下

  • @PostConstruct 、@PreDestroy(javax.annotation)
  • InitializingBean#afterPropertiesSet 、DisposableBean#destroy 回调接口
  • 自定义 init() 、destroy() 方法

怎么去记这个顺序呢?这里以初始化来举例。
可以这样理解,越标准的越先执行,那么属于java规范的@PostConstruct首先执行;而后spring接口InitializingBean#afterPropertiesSet ,最后才是自定义的方法。
销毁时同理,所以执行顺序依次是属于java规范的的@PreDestroy、接口DisposableBean#destroy、最后自定义destroy方法。

钩子方法执行时机

钩子方法在属性依赖注入完毕后执行,并且操作的raw bean reference,而不是proxy bean,这样可以使生命周期方法与proxy解耦。

自定义init、destory举例

  • xml中定义
    • <bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
  • @Bean注解中指定
    • 如下
public class BeanOne {

    public void init() {
        // initialization logic
    }
}
public class BeanTwo {

    public void cleanup() {
        // destruction logic
    }
}

@Configuration
public class AppConfig {

    @Bean(initMethod = "init")
    public BeanOne beanOne() {
        return new BeanOne();
    }

    @Bean(destroyMethod = "cleanup")
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}

生命周期钩子方法的实现原理

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
先注入依赖属性,然后执行生命周期钩子方法。

//注入依赖属性,@AutoWired、@Resource
populateBean(beanName, mbd, instanceWrapper);
// 执行生命周期钩子方法
exposedObject = initializeBean(beanName, exposedObject, mbd);

继续跟踪AbstractAutowireCapableBeanFactory#initializeBean

//通过CommonAnnotationBeanPostProcessor执行的@PostConstruct、@PreDestroy
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
// 执行InitializingBean、自定义钩子方法
invokeInitMethods(beanName, wrappedBean, mbd);

上边两行分别对应下边的两个标题 @PostConstruct的实现原理InitializingBean、自定义init方法实现原理

@PostConstruct的实现原理

AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization,执行了BeanPostProcessor#postProcessBeforeInitialization

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
			throws BeansException {

		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			//
			Object current = processor.postProcessBeforeInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}

是哪个BeanPostProcessor处理了@PostConstruct呢?CommonAnnotationBeanPostProcessor。

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

CommonAnnotationBeanPostProcessor是InitDestroyAnnotationBeanPostProcessor的子类,InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
		metadata.invokeInitMethods(bean, beanName);
	}

findLifecycleMetadata内部调了buildLifecycleMetadata,buildLifecycleMetadata里循环获取加了@PostConstruct、@PreDestory的方法,先获取当前类、再获取父类、再获取上一级父类直到Object,而且执行时机是先执行父类的方法、再执行子类的方法。

private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
		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<>();

			ReflectionUtils.doWithLocalMethods(targetClass, method -> {
				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);
					}
				}
				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 new LifecycleMetadata(clazz, initMethods, destroyMethods);
	}

初始化执行的原理很简单,就是反射执行初始化方法。
org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.LifecycleElement#invoke

public void invoke(Object target) throws Throwable {
	ReflectionUtils.makeAccessible(this.method);
	//反射执行初始化方法
	this.method.invoke(target, (Object[]) null);
}

InitializingBean、自定义init方法实现原理

继续跟踪AbstractAutowireCapableBeanFactory#invokeInitMethods

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
			throws Throwable {

		boolean isInitializingBean = (bean instanceof InitializingBean);
		if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
		          //执行InitializingBean#afterPropertiesSet
				((InitializingBean) bean).afterPropertiesSet();
		}

		if (mbd != null && bean.getClass() != NullBean.class) {
			String initMethodName = mbd.getInitMethodName();
			if (StringUtils.hasLength(initMethodName) &&
					!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
					!mbd.isExternallyManagedInitMethod(initMethodName)) {
					//执行自定义的init方法
				invokeCustomInitMethod(beanName, bean, mbd);
			}
		}
	}

自定义init方法是怎么执行的呢?AbstractAutowireCapableBeanFactory#invokeCustomInitMethod,拣重点看,也是反射执行方法

protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd)
			throws Throwable {

		String initMethodName = mbd.getInitMethodName();
		Assert.state(initMethodName != null, "No init method set");
		Method initMethod = (mbd.isNonPublicAccessAllowed() ?
		BeanUtils.findMethod(bean.getClass(), initMethodName) :
		ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName));
		Method methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(initMethod);
		//反射执行方法
		methodToInvoke.invoke(bean);
}

总结

  • @PostConstruct通过CommonAnnotationBeanPostProcessor#postProcessBeforeInitialization执行,postProcessBeforeInitialization在任何初始化方法(比如InitializingBean#afterPropertiesSet、自定义初始化方法)之前执行,所以@PostConstruct先执行,而后才是InitializingBean及自定义方法。
  • InitializingBean#afterPropertiesSet、自定义init通过AbstractAutowireCapableBeanFactory#invokeInitMethods实现。

源代码: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

参考

spring文档

以上是关于spring 声明周期钩子方法的主要内容,如果未能解决你的问题,请参考以下文章

2020 -02- 07组件与模板

Vue 生命周期钩子解读

Vue生命周期内的钩子方法

了解angularjs中的生命周期钩子函数$onInit,$onChange,$onDestory,$postLink

Spring Bean声明周期

AngularJS 纯 ng-controller 生命周期钩子