spring 之 init-method & InitializingBean
阿新 • • 發佈:2017-11-11
設置 diff object eof off gist n) rri clas
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 inclass 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