在Spring框架中,@PostConstruct注解、init-method属性、以及afterPropertiesSet()方法通常用于初始化Bean的逻辑。它们都提供了在Bean创建和初始化完成后执行的方法,但执行顺序有所不同。
想要知道@PostConstruct、init-method、afterPropertiesSet()的执行顺序,只要搞明白它们各自在什么时候被谁调用就行了。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import org.springframework.beans.factory.InitializingBean; import javax.annotation.PostConstruct; public class Foo implements InitializingBean { public void init(){ System.out.println( "执行了init生命周期的初始化回调" ); } @PostConstruct public void postConstruct(){ System.out.println( "执行了postConstruct生命周期的初始化回调" ); } @Override public void afterPropertiesSet() { System.out.println( "执行了afterPropertiesSet生命周期的初始化回调" ); } } |
1 2 3 4 5 6 7 8 9 10 | import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class InitConfiguration { @Bean (initMethod = "init" ) public Foo getInitMethodBean() { return new Foo(); } } |
执行启动类,可以看到在控制台中输出:
1 2 3 | 执行了postConstruct生命周期的初始化回调 执行了afterPropertiesSet生命周期的初始化回调 执行了init生命周期的初始化回调 |
@PostConstruct是Java EE 5引入的一个注解,它用于标记一个方法,该方法会在依赖注入完成后自动执行。这意味着,一旦Spring容器完成了Bean的实例化和属性赋值,就会调用这个方法。通常,我们会在这个方法中做一些初始化工作,比如启动服务、初始化数据库连接等。
init-method属性是Spring Bean的一个属性,它允许我们指定一个初始化方法。这个方法会在Bean实例化并完成属性注入后自动执行。与@PostConstruct注解不同的是,init-method属性并不依赖于Spring容器,因此可以在没有Spring的环境中运行。
afterPropertiesSet是SpringFramework中的一个初始化方法,它属于 InitializingBean接口的一部分。当bean的所有属性被Spring容器设置之后,这个方法会被自动调用。它允许开发者在bean属性设置完成之后执行一些特定的操作,如数据库连接池的初始化等。这个方法是在执行其他初始化方法之前被调用的。
源码分析:
通过断点调试发现几个初始化方法都定位到AbstractAutowireCapableBeanFactory的initializeBean方法中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | protected Object initializeBean( final String beanName, final Object bean, RootBeanDefinition mbd) { if (System.getSecurityManager() != null ) { AccessController.doPrivileged( new PrivilegedAction @Override public Object run() { invokeAwareMethods(beanName, bean); return null ; } }, getAccessControlContext()); } else { invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { // 此处执行的是@PostConstruct注解的方法 InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization 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; } |
执行afterPropertiesSet和init-method方法,在invokeInitMethods方法里面,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable { boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod( "afterPropertiesSet" ))) { if (logger.isDebugEnabled()) { logger.debug( "Invoking afterPropertiesSet() on bean with name '" + beanName + "'" ); } if (System.getSecurityManager() != null ) { try { AccessController.doPrivileged( new PrivilegedExceptionAction @Override public Object run() throws Exception { ((InitializingBean) bean).afterPropertiesSet(); return null ; } }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { // 执行afterPropertiesSet方法 ((InitializingBean) bean).afterPropertiesSet(); } } if (mbd != null ) { String initMethodName = mbd.getInitMethodName(); if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet" .equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { // 执行自定义的init-method方法 invokeCustomInitMethod(beanName, bean, mbd); } } } |
最终的结论是:@PostConstruct > afterPropertiesSet() > initMethod()的顺序
往期面试题:
Java面试题:SimpleDateFormat是线程安全的吗?使用时应该注意什么?
Java面试题:细数ThreadLocal大坑,内存泄露本可避免
Java面试题:为什么HashMap不建议使用对象作为Key?
Java面试题:你知道Spring的IOC吗?那么,它为什么这么重要呢?