SpringBoot日常:Spring之@PostConstruct解析
简介
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.CommonAnnotationBeanPostProcessor
public 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 {
@Autowired
private TestAutowired testAutowired;
public TagPostConstruct(){
log.info("TagPostConstruct构造方法------run");
}
@PostConstruct
public void myPostConstruct() {
log.info("PostConstruct------run");
}
}
@Slf4j
@Component
public class TestAutowired {
public TestAutowired(){
log.info("TestAutowired构造方法------run");
}
}
运行示例
可以看出@PostConstruct注解在整个Bean初始化中执行的顺序:@Constructor(构造方法)-> @Autowired(依赖注入)-> @PostConstruct(注解的方法);