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: 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的主要内容,如果未能解决你的问题,请参考以下文章

[死磕 Spring 29/43] --- IOC 之 深入分析 InitializingBean 和 init-method

[死磕 Spring 29/43] --- IOC 之 深入分析 InitializingBean 和 init-method

如何控制 Spring 中 bean init-method 调用的顺序?

?spring中afterPropertiesSet方法与init-method配置描述

Spring Bean init-method 和 destroy-method实例

Spring InitializingBean和init-method