ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Spring中你应该要知道的initMethod

2026/9/13 13:54:01 拓冰建站 浏览量
Spring中你应该要知道的initMethod

文章目录

  • 功能
  • 源码

功能

之前的文章中由解析过@PostConstruct/@PreDestroy,他们也是initMethod的一种形式,注解方式是后来才加入的,在源码中他们的命名都是一样的名字,都叫initMethod,不过他们却是有着很大的差别,并且他们的执行顺序也不相同,@PostConstruct > afterPropertiesSet >initMethod

xml配置方式,例如:

<bean id="xx" class="xxxxx" init-method="" destroy-method="">

注解配置

@Component
public class CustomConfig7 {@PostConstructpublic void t() {System.out.println("customConfig7 init");}@PostConstructpublic void t2() {System.out.println("customConfig7 init2");}@PreDestroypublic void d() {System.out.println("customConfig7 destroy");}@PreDestroypublic void d2() {System.out.println("customConfig7 destroy2");}
}

或者是

@Configuration
public class CustomConfig8 {@Bean(initMethod = "initTest", destroyMethod = "destroyTest")public CustomTest4 get() {return new CustomTest4();}public static class CustomTest4{public void initTest() {System.out.println("init 初始化。");}public void destroyTest() {System.out.println("destroy exec");}}
}

而在之前的文章中,还有一种方式,就是手动构建beanDefinition对象,然后手动设置到定义里面,如:

@Component
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {GenericBeanDefinition customBean = new GenericBeanDefinition();customBean.setBeanClassName(CustomTest4.class.getName());// 设置初始化方法customBean.setInitMethodName("initTest");// 设置销毁方法customBean.setInitMethodName("destroyTest");// 设置自定义bean的classcustomBean.setBeanClass(CustomTest4.class);// 设置自动注入customBean.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);((BeanDefinitionRegistry)beanFactory).registerBeanDefinition("customTestBean4", customBean);// 提前实例化Object customTestBean = beanFactory.getBean("customTestBean4");System.out.println("自定义bean:" + customTestBean);}
}

这最后这一种方法虽然用不到,但是可以作为了解,可以尝试不一样的方法去完成也是不错的。

源码

同样还是bean初始化方法,位置:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)

在这里插入图片描述

image-20231222213821105

initMethod方法调用和@PostConstruct是并不是同一个入口,它是在afterPropertiesSet方法调用之后执行的。

if (mbd != null && bean.getClass() != NullBean.class) {// 获取initMethod,// 就是:customBean.setInitMethodName("initTest");// 或者:@Bean(initMethod = "initTest", destroyMethod = "destroyTest")String initMethodName = mbd.getInitMethodName();if (StringUtils.hasLength(initMethodName) &&// 这里排除 InitializingBean.afterPropertiesSet 方法!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&// 和 @PostConstruct 方法!mbd.isExternallyManagedInitMethod(initMethodName)) {invokeCustomInitMethod(beanName, bean, mbd);}}

这里的逻辑含义是:

这些初始化回调方法只能执行一次,多次初始化能力赋予,也只能执行一次,且生效的是第一次。

下面进入真正执行的地方:

protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd)throws Throwable {
// 获取设置的initMethodString initMethodName = mbd.getInitMethodName();Assert.state(initMethodName != null, "No init method set");// 获取initMethod的方法对象,因为后面要进行反射操作// 这里不管它怎么校验,它都能获取私有方法(private)// 总感觉这有点多此一举,可能我没理解到Method initMethod = (mbd.isNonPublicAccessAllowed() ?BeanUtils.findMethod(bean.getClass(), initMethodName) :ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName));if (initMethod == null) {if (mbd.isEnforceInitMethod()) {throw new BeanDefinitionValidationException("Could not find an init method named '" +initMethodName + "' on bean with name '" + beanName + "'");}else {if (logger.isTraceEnabled()) {logger.trace("No default init method named '" + initMethodName +"' found on bean with name '" + beanName + "'");}// Ignore non-existent default lifecycle methods.return;}}if (logger.isTraceEnabled()) {logger.trace("Invoking init method  '" + initMethodName + "' on bean with name '" + beanName + "'");}// 这个反射工具是获取更加详细的方法信息,// 如果你的这个初始化方法是一个接口方法,那么他会找到你的接口类,这在后面接口判断中可能会有用Method methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(initMethod);// 又是AccessController.doPrivileged ,用于访问特权if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {ReflectionUtils.makeAccessible(methodToInvoke);return null;});try {AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () ->methodToInvoke.invoke(bean), getAccessControlContext());}catch (PrivilegedActionException pae) {InvocationTargetException ex = (InvocationTargetException) pae.getException();throw ex.getTargetException();}}else {try {// 一般是走这里的,两边都一样// makeAccessible 等效于 initMethod.setAccessible(true)ReflectionUtils.makeAccessible(methodToInvoke);// 直接反射调用methodToInvoke.invoke(bean);}catch (InvocationTargetException ex) {throw ex.getTargetException();}}}