spring 之 init-method & InitializingBean
Posted 10年 Java程序员,硬核人生!勇往直前,永不退缩!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring 之 init-method & InitializingBean相关的知识,希望对你有一定的参考价值。
init-method 是bean (第一次)实例化的时候被调用的。
先看个异常:
INFO: Overriding bean definition for bean ‘office‘ with a different definition: replacing [Generic bean: class [com.baobaotao.Office]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\code\ws\spring\hz\spring-learn\target\classes\com\baobaotao\Office.class]] with [Generic bean: class [com.baobaotao.Office]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [beans.xml]] Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘boss‘ defined in class path resource [beans.xml]: Invocation of init method failed; nested exception is org.springframework.beans.factory.support.BeanDefinitionValidationException: Couldn‘t find an init method named ‘initBoss‘ on bean with name ‘boss‘ at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1589) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:554) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1081) at AnnoIoCTest.main(AnnoIoCTest.java:10) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) Caused by: org.springframework.beans.factory.support.BeanDefinitionValidationException: Couldn‘t find an init method named ‘initBoss‘ on bean with name ‘boss‘ at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1677) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1656) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1585) ... 13 more
从 AbstractApplicationContext.getBean 调用,可见,它是在 getBean 阶段被调用的。
再看位于AbstractAutowireCapableBeanFactory的源码:
protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable { String initMethodName = mbd.getInitMethodName();
//默认 nonPublicAccessAllowed 是true , spring 仅仅是根据 配置的名字去 对应的class 中寻找同名方法, 至于是不是 static,public,有没有返回值 都不要紧 final Method initMethod = mbd.isNonPublicAccessAllowed()?BeanUtils.findMethod(bean.getClass(), initMethodName, new Class[0]):ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName, new Class[0]); if(initMethod == null) { // 如果那个方法不符合 约定(比如,如果方法有一个参数等等), 那么这里就会返回 null if(mbd.isEnforceInitMethod()) { throw new BeanDefinitionValidationException("Couldn\‘t find an init method named \‘" + initMethodName + "\‘ on bean with name \‘" + beanName + "\‘"); } else { if(this.logger.isDebugEnabled()) { this.logger.debug("No default init method named \‘" + initMethodName + "\‘ found on bean with name \‘" + beanName + "\‘"); } } } else { if(this.logger.isDebugEnabled()) { this.logger.debug("Invoking init method \‘" + initMethodName + "\‘ on bean with name \‘" + beanName + "\‘"); } if(System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedExceptionAction() { public Object run() throws Exception { ReflectionUtils.makeAccessible(initMethod); return null; } }); try { AccessController.doPrivileged(new PrivilegedExceptionAction() { public Object run() throws Exception { initMethod.invoke(bean, new Object[0]); return null; } }, this.getAccessControlContext()); } catch (PrivilegedActionException var9) { InvocationTargetException ex = (InvocationTargetException)var9.getException(); throw ex.getTargetException(); } } else { try { ReflectionUtils.makeAccessible(initMethod); // 方法是不是 public 不要紧, 这里会通过反射修改其可见性 initMethod.invoke(bean, new Object[0]); // new Object[0] 表明了 那个方法不能有任何参数, 否则就会出现异常。 } catch (InvocationTargetException var8) { throw var8.getTargetException(); } } } }
init-method & InitializingBean
InitializingBean的原理并不复杂。 首先它是一个接口,它提供了 方法: public void afterPropertiesSet() throws Exception 。 如果我们的类实现了 它,只要我们的类归spring 管理, 那么spring 会在第一次实例化 这个类的bean的时候, 进行这些相关接口的 方法的初始化。 从afterPropertiesSet名字也可知, 这个方法是在 bean完成了所有 属性的设置后 才进行调用的。 也就是说, 方法的初始化会 晚于 属性的初始化。
init-method 和 InitializingBean 是类似的,但其原理还是有所不同的,参见AbstractAutowireCapableBeanFactory 的源码可知:
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(this.logger.isDebugEnabled()) { this.logger.debug("Invoking afterPropertiesSet() on bean with name \‘" + beanName + "\‘"); } if(System.getSecurityManager() != null) { try { AccessController.doPrivileged(new PrivilegedExceptionAction() { public Object run() throws Exception { ((InitializingBean)bean).afterPropertiesSet(); return null; } }, this.getAccessControlContext()); } catch (PrivilegedActionException var6) { throw var6.getException(); } } else { ((InitializingBean)bean).afterPropertiesSet(); // 先是进行 InitializationBean 的初始化 } } if(mbd != null) { String initMethodName = mbd.getInitMethodName(); // 然后, 如果这个bean 存在 init 方法,并且如果它非InitializationBean且方法名非 aferPropertiesSet,并且这个bean不是外部管理的 ,,那么执行它。 if(initMethodName != null && (!isInitializingBean || !"afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { this.invokeCustomInitMethod(beanName, bean, mbd); } } }
以上是关于spring 之 init-method & InitializingBean的主要内容,如果未能解决你的问题,请参考以下文章
[死磕 Spring 29/43] --- IOC 之 深入分析 InitializingBean 和 init-method
[死磕 Spring 29/43] --- IOC 之 深入分析 InitializingBean 和 init-method
如何控制 Spring 中 bean init-method 调用的顺序?
?spring中afterPropertiesSet方法与init-method配置描述