zoukankan      html  css  js  c++  java
  • Spring InitializingBean init-method @PostConstruct 执行顺序

    Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,常用的设定方式有以下三种:
     
    通过实现 InitializingBean/DisposableBean 接口来定制初始化之后/销毁之前的操作方法;
    通过 元素的 init-method/destroy-method属性指定初始化之后 /销毁之前调用的操作方法;
    在指定方法上加上@PostConstruct 或@PreDestroy注解来制定该方法是在初始化之后还是销毁之前调用。 
    它们的先后顺序是怎样的,我们用下面的测试代码来验证
     
    package com.example;
    
    public class InitSequenceBean implements InitializingBean {   
        
        public InitSequenceBean() {   
           System.out.println("InitSequenceBean: constructor");   
        }   
          
        @PostConstruct  
        public void postConstruct() {   
           System.out.println("InitSequenceBean: postConstruct");   
        }   
          
        public void initMethod() {   
           System.out.println("InitSequenceBean: init-method");   
        }   
          
        @Override  
        public void afterPropertiesSet() throws Exception {   
           System.out.println("InitSequenceBean: afterPropertiesSet");   
        }   
    }  
     

    配置如下

    <bean id="initSequenceBean " class="com.example.InitSequenceBean" init-method="initMethod"/>

    好了,我们启动Spring容器,观察输出结果

     
    InitSequenceBean: constructor
    
    InitSequenceBean: postConstruct
    
    InitSequenceBean: afterPropertiesSet
    
    InitSequenceBean: init-method
     
    通过上述输出结果,三者的先后顺序也就一目了然了:
     
    Constructor > @PostConstruct > InitializingBean > init-method
    先大致分析下为什么会出现这些的结果:构造器(Constructor)被率先调用毋庸置疑,InitializingBean先于init-method我们也可以理解(在也谈Spring容器的生命周期中已经讨论过),但是PostConstruct为何率先于InitializingBean执行呢?
     
    我们再次带着这个疑问去查看Spring源代码来一探究竟。
    通过Debug并查看调用栈,我们发现了这个类org.springframework.context.annotation.CommonAnnotationBeanPostProcessor,从命名上,我们就可以得到某些信息——这是一个BeanPostProcessor。想到了什么?在也谈Spring容器的生命周期中,我们提到过BeanPostProcessor的postProcessBeforeInitialization是在Bean生命周期中afterPropertiesSet和init-method之前执被调用的。
     
    再次观察CommonAnnotationBeanPostProcessor这个类,它继承自InitDestroyAnnotationBeanPostProcessor。InitDestroyAnnotationBeanPostProcessor顾名思义,就是在Bean初始化和销毁的时候所作的一个前置/后置处理器。
     
    通过查看InitDestroyAnnotationBeanPostProcessor类下的postProcessBeforeInitialization方法:
     
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {   
           LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());   
           try {   
               metadata.invokeInitMethods(bean, beanName);   
           }   
           catch (InvocationTargetException ex) {   
               throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());   
           }   
           catch (Throwable ex) {   
               throw new BeanCreationException(beanName, "Couldn't invoke init method", ex);   
           }   
            return bean;   
        }  
      
     

    查看findLifecycleMetadata方法,继而我们跟踪到buildLifecycleMetadata这个方法体中,看下buildLifecycleMetadata这个方法体的内容:

     
    private LifecycleMetadata buildLifecycleMetadata(final Class clazz) {   
           final LifecycleMetadata newMetadata = new LifecycleMetadata();   
           final boolean debug = logger.isDebugEnabled();   
           ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {   
               public void doWith(Method method) {   
                  if (initAnnotationType != null) {   
                      if (method.getAnnotation(initAnnotationType) != null) {   
                         newMetadata.addInitMethod(method);   
                         if (debug) {   
                             logger.debug("Found init method on class [" + clazz.getName() + "]: " + method);   
                         }   
                      }   
                  }   
                  if (destroyAnnotationType != null) {   
                      if (method.getAnnotation(destroyAnnotationType) != null) {   
                         newMetadata.addDestroyMethod(method);   
                         if (debug) {   
                             logger.debug("Found destroy method on class [" + clazz.getName() + "]: " + method);   
                         }   
                      }   
                  }   
               }   
           });   
           return newMetadata;   
    }  
     
    分析这段代码发现,在这里会去判断某方法有没有被initAnnotationType/destroyAnnotationType注释,如果有,则添加到init/destroy队列中,后续一一执行。
     
    initAnnotationType/destroyAnnotationType注释是什么呢,我们在CommonAnnotationBeanPostProcessor的构造函数中看到下面这段代码:
     
    public CommonAnnotationBeanPostProcessor() {   
           setOrder(Ordered.LOWEST_PRECEDENCE - 3);   
           setInitAnnotationType(PostConstruct.class);   
           setDestroyAnnotationType(PreDestroy.class);   
           ignoreResourceType("javax.xml.ws.WebServiceContext");   
    }  
     

    一切都清晰了吧。一言以蔽之,@PostConstruct注解后的方法在BeanPostProcessor前置处理器中就被执行了,所以当然要先于InitializingBean和init-method执行了。

    接下来看看为什么InitializingBean先于init-method执行,通过查看spring的加载bean的源码类(AbstractAutowireCapableBeanFactory)可看出其中奥妙

    AbstractAutowireCapableBeanFactory类中的invokeInitMethods讲解的非常清楚,源码如下:

     
    protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable {
        //判断该bean是否实现了实现了InitializingBean接口,如果实现了InitializingBean接口,则只掉调用bean的afterPropertiesSet方法
        boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            if (logger.isDebugEnabled()) {
                logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
            }
             
            if (System.getSecurityManager() != null) {
                try {
                    AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                        public Object run() throws Exception {
                            //直接调用afterPropertiesSet
                            ((InitializingBean) bean).afterPropertiesSet();
                            return null;
                        }
                    },getAccessControlContext());
                } catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }                
            else {
                //直接调用afterPropertiesSet
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }
        if (mbd != null) {
            String initMethodName = mbd.getInitMethodName();
            //判断是否指定了init-method方法,如果指定了init-method方法,则再调用制定的init-method
            if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
                //进一步查看该方法的源码,可以发现init-method方法中指定的方法是通过反射实现
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
    }
     

    总结

    1:spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中同过init-method指定,两种方式可以同时使用

    2:实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率相对来说要高点。但是init-method方式消除了对spring的依赖

    3:如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。

    最后,给出本文的结论,Bean在实例化的过程中:
     
    Constructor > @PostConstruct > InitializingBean > init-method
  • 相关阅读:
    几个新角色:数据科学家、数据分析师、数据(算法)工程师
    人类投资经理再也无法击败电脑的时代终将到来了...
    Action Results in Web API 2
    Multiple actions were found that match the request in Web Api
    Routing in ASP.NET Web API
    how to create an asp.net web api project in visual studio 2017
    网站漏洞扫描工具
    How does asp.net web api work?
    asp.net web api history and how does it work?
    What is the difference between a web API and a web service?
  • 原文地址:https://www.cnblogs.com/liuys635/p/13274247.html
Copyright © 2011-2022 走看看