简介
spring的Bean在创建的时候会进行初始化,而初始化过程会解析出@PostConstruct注解的方法,并反射调用该方法。
@PostConstruct 的使用和特点
- 只有一个非静态方法能使用此注解;
- 被注解的方法不得有任何参数;
- 被注解的方法返回值必须为void;
- 被注解的方法不得抛出已检查异常;
- 被注解的方法只会被执行一次;
源码分析
在Bean创建完成后,进行初始化的过程中,主要包含了初始化的前置、后置处理,以及初始化方法的调用。@PostConstruct注解将在applyBeanPostProcessorsBeforeInitialization这个前置处理
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;//遍历了在spring启动过程中被注册的BeanPostProcessor接口,for (BeanPostProcessor processor : getBeanPostProcessors()) {//调用其前置方法。Object current = processor.postProcessBeforeInitialization(result, beanName);if (current == null) {return result;}result = current;}return result;
}
@PostConstruct注解是会被一个专门的BeanPostProcessor接口的具体实现类来处理的InitDestroyAnnotationBeanPostProcessor
//org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// 元数据解析 注解的初始化方法也会在这里被找到LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());try {// 触发初始化方法metadata.invokeInitMethods(bean, beanName);}catch (InvocationTargetException ex) {throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());}catch (Throwable ex) {throw new BeanCreationException(beanName, "Failed to invoke init method", ex);}return bean;
}
public void invokeInitMethods(Object target, String beanName) throws Throwable {Collection<LifecycleElement> checkedInitMethods = this.checkedInitMethods;Collection<LifecycleElement> initMethodsToIterate =(checkedInitMethods != null ? checkedInitMethods : this.initMethods);if (!initMethodsToIterate.isEmpty()) {for (LifecycleElement element : initMethodsToIterate) {if (logger.isTraceEnabled()) {logger.trace("Invoking init method on bean '" + beanName + "': " + element.getMethod());}// 调用element.invoke(target);}}
}
注意:在CommonAnnotationBeanPostProcessor这个后置处理器的构造方法中,@PostConstruct注解被设置为了initAnnotationType的值
//org.springframework.context.annotation.CommonAnnotationBeanPostProcessorpublic CommonAnnotationBeanPostProcessor() {setOrder(Ordered.LOWEST_PRECEDENCE - 3);setInitAnnotationType(PostConstruct.class);setDestroyAnnotationType(PreDestroy.class);ignoreResourceType("javax.xml.ws.WebServiceContext");// java.naming module present on JDK 9+?if (jndiPresent) {this.jndiFactory = new SimpleJndiBeanFactory();}
}
应用场景
@PostConstruct不算一个扩展点,但是也有比较实用的应用场景,主要在Servlet初始化之前加载一些缓存数据,比如
- 数据字典,
- 读取properties配置文件
- …
代码示例
@Component
@Slf4j
public class TagPostConstruct {@Autowiredprivate TestAutowired testAutowired;public TagPostConstruct(){log.info("TagPostConstruct构造方法------run");}@PostConstructpublic void myPostConstruct() {log.info("PostConstruct------run");}
}@Slf4j
@Component
public class TestAutowired {public TestAutowired(){log.info("TestAutowired构造方法------run");}
}
运行示例
可以看出@PostConstruct注解在整个Bean初始化中执行的顺序:@Constructor(构造方法)-> @Autowired(依赖注入)-> @PostConstruct(注解的方法);