Spring中存在很多回调,但是执行他们的时机都不相同,也许大家用的最多的是InitializingBean.afterPropertiesSet
,这个方法的作用如名称一样,是bean初始化后执行的一个回调操作,而@PostConstruct
是initMethod
,初始化回调方法,它是在afterPropertiesSet
之前执行的,并且可以有多个@PostConstruct
和@PreDestory
,所以它的功能会更强大一点。
但需要注意的是,多个@PostConstruct
方法的顺序是:父类内从先到后,然后子类内从先到后,多个@PreDestory
方法的顺序是:子类内从先到后,父类内从先到后。
另外一个点:这里我说它是initMethod
是因为Spring本身对这个方法解析后的命名就叫initMethod
,保存在InitDestroyAnnotationBeanPostProcessor
中,真正意义上的initMethod
是保存在BeanDefinition
里的,并且只有一个,所以这个要区分一下。
如下示例:
@Component
public class CustomConfig7 {
@PostConstruct
public void t() {
System.out.println("customConfig7 init");
}
@PostConstruct
public void t2() {
System.out.println("customConfig7 init2");
}
@PreDestroy
public void d() {
System.out.println("customConfig7 destroy");
}
@PreDestroy
public void d2() {
System.out.println("customConfig7 destroy2");
}
}
它的解析和执行过程比较简单,很快就可以看完。
位置:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
这里它是调用了父类的方法postProcessMergedBeanDefinition
进行解析的, 下面findResourceMetadata
是依赖注入的注入点解析。
private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
// lifecycleMetadataCache 初始化时赋了初始值,所以这里不会走
if (this.lifecycleMetadataCache == null) {
return buildLifecycleMetadata(clazz);
}
// 从缓存种获取
// 双重判断
LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
if (metadata == null) {
synchronized (this.lifecycleMetadataCache) {
metadata = this.lifecycleMetadataCache.get(clazz);
if (metadata == null) {
// 解析class
metadata = buildLifecycleMetadata(clazz);
this.lifecycleMetadataCache.put(clazz, metadata);
}
return metadata;
}
}
return metadata;
}
这里会看到synchronized
,Spring创建bean,也就是bean的生命周期这个过程时单线程的,但它这里加了锁,这个问题,作为底层框架要保证线程安全,因为在业务场景下,并不都是单线程的,比如:
你定义了一个bean,简称A,同时你实现了接口InitializingBean
,在回调方法时,你开启了一个线程B,这个线程B可能做数据查询,数据缓存等,那么他需要redis,datasource,sqlSession这些,那么它也是从beanFactory
获取bean,这时Spring也在getBean
,线程B和Spring就已经并行了,就是应用本身是多线程的,作为底层的框架需要保证线程安全。
那接下来,我们看一下构建方法:
位置:org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#buildLifecycleMetadata
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
// 进行过滤
// initAnnotationType -> @PostConstruct
// destroyAnnotationType -> @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<>();
// Spring的反射工具,解析并遍历class里的方法
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
// 如果方法含有注解:@PostConstruct,就添加到currInitMethods
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,就添加到currDestroyMethods
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));
}
注意: 上面它是一个向上的循环,先顺序遍历当前类,再找父类,一直往上找,而有一点不一样:
initMethods.addAll(0, currInitMethods);
destroyMethods.addAll(currDestroyMethods);
initMethod
每次都放第一个,所以,父类的@PostConstruct
应该在子类之前执行,这个也和类的实例是一样的顺序。
destroyMethod
是顺序放入,执行顺序应该是子类先执行;
这个执行顺序,这里只是根据它放入的顺序的一个猜测,最终顺序还得看它执行的过程。
在初始化bean时,spring执行了一个后置处理器,而执行@PostConstruct/@PreDestroy就是在里面执行的
位置:org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization
LifecycleMetadata
是在解析时生成的对象,看一下它的属性:
它的执行如下,是通过遍历的方式,所以@PonstConstruct
执行的顺序是先执行父类的,然后执行子类的。
至于@PreDestroy
,它是在Spring容器关闭时执行的,有ApplicationContext
触发,位置:
org.springframework.context.support.AbstractApplicationContext#doClose
同样最终执行的位置和@PostConstruct
是同一个处理器,两个的代码几乎是一样的,都是直接遍历解析号的方法集,然后顺序执行,不过,这里先执行的是子类,然后才是父类的。