回答
在 Spring 中,@PostConstruct
、init-method
和 afterPropertiesSet
都是用于在 Bean 初始化阶段执行自定义初始化逻辑的方法。他们的执行顺序如下:
@PostConstruct
:首先是@PostConstruct
执行。@PostConstruct
属于JSR-250
标准注解,为 Java EE 标准中的注解。Spring 管理 Bean 的生命周期时会自动识别@PostConstruct
,并在属性注入完成之后执行。所以,@PostConstruct
执行时机比其他 Spring 专有的初始化方法更早。afterPropertiesSet
:是 Spring 专门为 Bean 的初始化设计的一个方法,定义在InitializingBean
接口中,用来定义一些初始化逻辑。Spring 在创建 Bean 的过程中会检查 Bean 是否实现了InitializingBean
接口,如果实现了则在@PostConstruct
方法执行完毕后调用afterPropertiesSet
方法。init-method
:在 Spring 配置文件(如 XML 配置)或者@Bean
注解中指定的方法,该方法会在前两个初始化步骤执行完毕后调用。
所以,三者的执行顺序为:@PostConstruct > InitializingBean > init-method
详解
使用一个例子来看看:
public class BeanInitTest implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet 执行");
}
@PostConstruct
public void postConstructMethod() {
System.out.println("@PostConstruct 执行");
}
public void initMethod(){
System.out.println("init-method 执行");
}
@Configuration
static class InitConfiguration {
@Bean(initMethod = "initMethod")
public BeanInitTest getInitMethodBean() {
return new BeanInitTest();
}
}
}
然后启动下应用程序,查看执行结果:
我们再从源码层次来看,通过 debug 的跟踪,可以发现这几个初始化的方法都是在 AbstractAutowireCapableBeanFactory#initializeBean()
中:
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 这里是执行 @PostConstruct 的方法
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// 这里执行的是 afterPropertiesSet 和 init-method 方法
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
从这个方法可以看出,先执行 @PostConstruct
的方法,然后调用 invokeInitMethods()
执行afterPropertiesSet
和 init-method
:
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
// 执行 afterPropertiesSet
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
// 执行 afterPropertiesSet
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
// 执行 init-method
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。