Spring中@PostConstruct的实现方法
前言
大多数java
程序员都使用过@PostConstruct
注解,它的作用就是在Bean
初始化完成后执行,相当于我们常说的init()
方法。但是我们看@PostConstruct
只有单单的一个注解,它到底是如何实现在Bean
初始化完成后就被调用的呢?
源码分析
我们通过idea
搜索发现,只有CommonAnnotationBeanPostProcessor
这个类使用了@PostConstruct
:
public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor implementsInstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable { public CommonAnnotationBeanPostProcessor() { this.setOrder(2147483644); // 给initAnnotationType赋值 this.setInitAnnotationType(PostConstruct.class); this.setDestroyAnnotationType(PreDestroy.class); this.ignoreResourceType("javax.xml.ws.WebServiceContext"); if (jndiPresent) { this.jndiFactory = new SimpleJndiBeanFactory(); } } }
通过源码发现,这显然就是一个BeanPostProcessor
的子类啊,它在Spring
的生命周期中起作用,所以我们可以重点关注postProcessBeforeInitialization()
方法:
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // 获取被@PostConstruct注解的方法元数据 InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata metadata = this.findLifecycleMetadata(bean.getClass()); try { // 调用目标方法 metadata.invokeInitMethods(bean, beanName); return bean; } catch (InvocationTargetException var5) { throw new BeanCreationException(beanName, "Invocation of init method failed", var5.getTargetException()); } catch (Throwable var6) { throw new BeanCreationException(beanName, "Failed to invoke init method", var6); } }
当Bean
初始化完成后,postProcessBeforeInitialization()
方法将被调用,所有被注解了@PostConstruct
都会被调用,无论这个方法是在父类还是子类中:
private InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata findLifecycleMetadata(Class<?> clazz) { // 如果缓存为null,那么构建缓存;这个缓存是存储Bean中所有被@PostConstruct注解的方法元数据 if (this.lifecycleMetadataCache == null) { // 构建缓存 return this.buildLifecycleMetadata(clazz); } else { // 如果缓存不为null,那么从缓存中取出所有被@PostConstruct注解的方法元数据 InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata metadata =(InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata)this.lifecycleMetadataCache.get(clazz); // 如果缓存中取出来的元数据为null,这段代码这种写法是考虑到现在有多个线程,用了加锁操作保证只有一个线程去构建缓存buildLifecycleMetadata() if (metadata == null) { synchronized(this.lifecycleMetadataCache) { metadata = (InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata)this.lifecycleMetadataCache.get(clazz); // 如果此时还没拿到元数据,就去构建缓存 if (metadata == null) { // 收集好元数据 metadata = this.buildLifecycleMetadata(clazz); // 构建缓存 this.lifecycleMetadataCache.put(clazz, metadata); } // 返回元数据 return metadata; } } else { // 返回元数据 return metadata; } } }
1.缓存为null的情况下直接构建缓存;
2.缓存不为null,就从缓存中取被注解的方法元数据,没取到就构建缓存;
所以我们重点看看缓存是如何构建的:
private InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata buildLifecycleMetadata(Class<?> clazz) { // 如果这个类一定没有被initAnnotationType或destroyAnnotationType注解 // 此时initAnnotationType就是我们的@PostConstruct注解 if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) { return this.emptyLifecycleMetadata; } else { // 准备好列表来装被注解的方法 List<InitDestroyAnnotationBeanPostProcessor.LifecycleElement> initMethods = new ArrayList(); List<InitDestroyAnnotationBeanPostProcessor.LifecycleElement> destroyMethods = new ArrayList(); Class targetClass = clazz; // 准备循环向上遍历所有的父类 do { List<InitDestroyAnnotationBeanPostProcessor.LifecycleElement> currInitMethods = new ArrayList(); List<InitDestroyAnnotationBeanPostProcessor.LifecycleElement> currDestroyMethods = new ArrayList(); ReflectionUtils.doWithLocalMethods(targetClass, (method) -> { // 如果这个方法被@PostConstruct注解,那么就构建元数据并放进currInitMethods中 if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) { InitDestroyAnnotationBeanPostProcessor.LifecycleElement element = newInitDestroyAnnotationBeanPostProcessor.LifecycleElement(method); currInitMethods.add(element); if (this.logger.isTraceEnabled()) { this.logger.trace("Found init method on class [" + clazz.getName() + "]: " + method); } } // 下面是判断是否被destroyAnnotationType注解 if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) { currDestroyMethods.add(new InitDestroyAnnotationBeanPostProcessor.LifecycleElement(method)); if (this.logger.isTraceEnabled()) { this.logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method); } } }); // 先把当前类被注解的方法元数据列表放进initMethods头部 initMethods.addAll(0, currInitMethods); destroyMethods.addAll(currDestroyMethods); // 获取当前类的父类 targetClass = targetClass.getSuperclass(); // 准备遍历父类是否有被注解的方法,有的话收集好放进initMethods头部 } while(targetClass != null && targetClass != Object.class); // 返回构建好的元数据 return initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata : newInitDestroyAnnotationBeanPostProcessor.LifecycleMetadata(clazz, initMethods, destroyMethods); } }
所以我们通过上述源码的分析,最后得出以下结论:
1.
Bean
的父类方法也可以使用@PostConstruct
注解;2.执行的时候是先执行被
@PostConstruct
注解的父类方法,再执行被@PostConstruct
注解的子类方法;3.被
@PostConstruct
注解的方法不能有任何参数,可以通过new InitDestroyAnnotationBeanPostProcessor.LifecycleElement(method)
源码验证;
以上就是Spring中@PostConstruct的实现方法的详细内容,更多关于Spring @PostConstruct实现的资料请关注脚本之家其它相关文章!
相关文章
spring boot使用自定义配置的线程池执行Async异步任务
这篇文章主要介绍了spring boot使用自定义配置的线程池执行Async异步任务,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧2018-01-01Windows下 IDEA编译调试 hive2.3.9的过程解析
这篇文章主要介绍了Windows下 IDEA编译调试 hive2.3.9的过程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2023-07-07
最新评论