1. 程式人生 > >spring 之 init-method & InitializingBean

spring 之 init-method & InitializingBean

設置 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 in
class path resource [beans.xml]: Invocation of init method failed; nested exception is org.springframework.beans.factory.support.BeanDefinitionValidationException: Couldnt 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: Couldnt 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