当前位置 博文首页 > 文章内容

    关于@PostConstruct、afterPropertiesSet和init-method的执行顺序

    作者:shunshunshun18 栏目:未分类 时间:2021-09-15 14:43:45

    本站于2023年9月4日。收到“大连君*****咨询有限公司”通知
    说我们IIS7站长博客,有一篇博文用了他们的图片。
    要求我们给他们一张图片6000元。要不然法院告我们

    为避免不必要的麻烦,IIS7站长博客,全站内容图片下架、并积极应诉
    博文内容全部不再显示,请需要相关资讯的站长朋友到必应搜索。谢谢!

    另祝:版权碰瓷诈骗团伙,早日弃暗投明。

    相关新闻:借版权之名、行诈骗之实,周某因犯诈骗罪被判处有期徒刑十一年六个月

    叹!百花齐放的时代,渐行渐远!



    @PostConstruct、init-method、afterPropertiesSet() 执行顺序

    想要知道 @PostConstruct、init-method、afterPropertiesSet() 的执行顺序,只要搞明白它们各自在什么时候被谁调用就行了。

    程序版本:Spring Boot 2.3.5.RELEASE

    准备好要验证的材料:

    public class Foo implements InitializingBean {
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("afterPropertiesSet()");
        }
        @PostConstruct
        public void init() {
            System.out.println("@PostConstruct");
        }
        private void initMethod() {
            System.out.println("initMethod()");
        }
    }
    
    @Configuration
    public class FooConfiguration {
        @Bean(initMethod = "initMethod")
        public Foo foo() {
            return new Foo();
        }
    }
    

    执行启动类,可以看到在控制台中输出:

    @PostConstruct

    afterPropertiesSet()

    initMethod()

    说明执行顺序是:@PostConstruct、afterPropertiesSet()、init-method

    接下来将跟着源码来了解为什么是这个顺序。

    @PostConstruct 标注的方法在何时被谁调用

    首先,在 init() 中打个断点,然后以 debug 的方式启动项目,得到下面的调用栈:

    init:23, Foo (com.xurk.init.foo)
    invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
    invoke:62, NativeMethodAccessorImpl (sun.reflect)
    invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
    invoke:498, Method (java.lang.reflect)
    invoke:389, InitDestroyAnnotationBeanPostProcessor$LifecycleElement (org.springframework.beans.factory.annotation)
    invokeInitMethods:333, InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata (org.springframework.beans.factory.annotation)
    postProcessBeforeInitialization:157, InitDestroyAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
    applyBeanPostProcessorsBeforeInitialization:415, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
    initializeBean:1786, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
    doCreateBean:594, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
    createBean:516, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
    lambda$doGetBean$0:324, AbstractBeanFactory (org.springframework.beans.factory.support)
    getObject:-1, 2103763750 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$169)
    getSingleton:234, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
    doGetBean:322, AbstractBeanFactory (org.springframework.beans.factory.support)
    getBean:202, AbstractBeanFactory (org.springframework.beans.factory.support)
    preInstantiateSingletons:897, DefaultListableBeanFactory (org.springframework.beans.factory.support)
    finishBeanFactoryInitialization:879, AbstractApplicationContext (org.springframework.context.support)
    refresh:551, AbstractApplicationContext (org.springframework.context.support)
    refresh:143, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
    refresh:758, SpringApplication (org.springframework.boot)
    refresh:750, SpringApplication (org.springframework.boot)
    refreshContext:405, SpringApplication (org.springframework.boot)
    run:315, SpringApplication (org.springframework.boot)
    run:1237, SpringApplication (org.springframework.boot)
    run:1226, SpringApplication (org.springframework.boot)
    main:14, InitApplication (com.xurk.init)
    

    从上往下看,跳过使用 sun.reflect 的方法,进入到第6行。

    public void invoke(Object target) throws Throwable {
       ReflectionUtils.makeAccessible(this.method);
       this.method.invoke(target, (Object[]) null);
    }

    很明显,这里是在通过反射调用某个对象的一个方法,并且这个“某个对象”就是我们定义的 Foo的实例对象了。

    那么这里的 method 又是在什么时候进行赋值的呢?

    invoke(...) 全路径是:

    org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.LifecycleElement#invoke

    并且 LifecycleElement 有且只有一个显示声明并且带参数的构造器,这个要传入构造器的参数正是 invoke(...) 使用的那个 Method 对象。

    接下来就是查一下,是谁在 new LifecycleElement 。

    于是定位到:

    org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#buildLifecycleMetadata

    private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
        if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {
            return this.emptyLifecycleMetadata;
        }
        List<LifecycleElement> initMethods = new ArrayList<>();
        List<LifecycleElement> destroyMethods = new ArrayList<>();
        Class<?> targetClass = clazz;
        do {
            final List<LifecycleElement> currInitMethods = new ArrayList<>();
            final List<LifecycleElement> currDestroyMethods = new ArrayList<>();
            ReflectionUtils.doWithLocalMethods(targetClass, method -> {
                if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
                    LifecycleElement element = new LifecycleElement(method);
                    currInitMethods.add(element);
                    if (logger.isTraceEnabled()) {
                        logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
                    }
                }
                if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
                    currDestroyMethods.add(new LifecycleElement(method));
                    if (logger.isTraceEnabled()) {
                        logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
                    }
                }
            });
            initMethods.addAll(0, currInitMethods);
            destroyMethods.addAll(currDestroyMethods);
            targetClass = targetClass.getSuperclass();
        }
        while (targetClass != null && targetClass != Object.class);
        return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
                new LifecycleMetadata(clazz, initMethods, destroyMethods));
    }
    

    第15-26行是在通过反射判断方法上是否存在某个注解,如果是的话就加到一个集合中,在最后用于构建 LifecycleMetadata 实例。

    在这里因为我们要查找的是被赋值的内容,所以在使用IDE进行查找时只要关注 write 相关的内容就行了。

    在这里插入图片描述

    到这里,已经知道了是在 CommonAnnotationBeanPostProcessor 的构造器中进行 set 的,也就是当创建 CommonAnnotationBeanPostProcessor 实例的时候就会进行赋值,并且 CommonAnnotationBeanPostProcessor 是 InitDestroyAnnotationBeanPostProcessor 的子类。

    在这里插入图片描述

    并且,CommonAnnotationBeanPostProcessor 构造器中调用的 setInitAnnotationType 其实是它父类的方法,实际是对 InitDestroyAnnotationBeanPostProcessor 的实例的 initAnnotationType 字段进行赋值。

    到这里已经可以明确 buildLifecycleMetadata(...) 中判断的正是 @PostConstruct。

    再回到buildLifecycleMetadata(...) ,查看其使用的 doWithLocalMethods(...) 的实现。

    public static void doWithLocalMethods(Class<?> clazz, MethodCallback mc) {
        Method[] methods = getDeclaredMethods(clazz, false);
        for (Method method : methods) {
            try {
                mc.doWith(method);
            }
            catch (IllegalAccessException ex) {
                throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
            }
        }
    }
    

    很简单,通过反射获得类的所有方法,然后调用一个函数接口的方法,这个函数接口的实现就是判断方法是不是被 @PostConstruct 标注,如果被标注的话放到一个名字叫 currInitMethods 的集合中。

    看到这里或许你已经意识到了, @PostConstruct 可以标注多个方法,并且因为反射获取方法时是根据声明顺序的、 currInitMethods 是 ArrayList,两者之间的顺序是一样的。

    好了,被 @PostConstruct 标注的方法已经找到放到集合中了,将被用来构建 LifecycleMetadata 实例了。

    buildLifecycleMetadata(...) 方法返回一个 LifecycleMetadata 实例,这个返回值中包含传入Class实例中得到的所有被 @PostConstruct 标注的 Method 实例,接下来要看看是谁在调用 buildLifecycleMetadata(...) 方法,看看它是怎么用的?

    追溯到 findLifecycleMetadata(...) 而 findLifecycleMetadata(...) 又有好几处被调用。

    在这里插入图片描述

    查看最早得到的方法调用栈,查到postProcessBeforeInitializatio(...) ,它又是再被谁调用?

    init:23, Foo (com.xurk.init.foo)
    invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
    invoke:62, NativeMethodAccessorImpl (sun.reflect)
    invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
    invoke:498, Method (java.lang.reflect)
    invoke:389, InitDestroyAnnotationBeanPostProcessor$LifecycleElement (org.springframework.beans.factory.annotation)
    invokeInitMethods:333, InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata (org.springframework.beans.factory.annotation)
    postProcessBeforeInitialization:157, InitDestroyAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
    applyBeanPostProcessorsBeforeInitialization:415, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
    initializeBean:1786, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
    

    跟着方法调用栈,我们来到

    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization

    @Override
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
          throws BeansException {
       Object result = existingBean;
       for (BeanPostProcessor processor : getBeanPostProcessors()) {
          Object current = processor.postProcessBeforeInitialization(result, beanName);
          if (current == null) {
             return result;
          }
          result = current;
       }
       return result;
    }
    

    在这个方法,遍历 BeanPostProcessors 集合并执行每个 BeanPostProcessor 的 postProcessBeforeInitialization(...) 方法。

    **那么 getBeanPostProcessors() 中的内容又是在什么时候放进去的呢?都有哪些内容?**可以通过 AnnotationConfigUtils和 CommonAnnotationBeanPostProcessor 查找,这里就不再赘述了。

    查找 applyBeanPostProcessorsBeforeInitialization(...) 的调用者,来到

    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean

    protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
       if (System.getSecurityManager() != null) {
          AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
             invokeAwareMethods(beanName, bean);
             return null;
          }, getAccessControlContext());
       }
       else {
          invokeAwareMethods(beanName, bean);
       }
       Object wrappedBean = bean;
       if (mbd == null || !mbd.isSynthetic()) {
          wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
       }
       try {
          invokeInitMethods(beanName, wrappedBean, mbd);
       }
       catch (Throwable ex) {
          throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null),
                beanName, "Invocation of init method failed", ex);
       }
       if (mbd == null || !mbd.isSynthetic()) {
          wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
       }
       return wrappedBean;
    }
    

    顾名思义,在这个方法中对 Bean 进行初始化。是在被注入前一定要经过的过程,到这里被 @PostConstruct 标注的方法执行已经完成了。至于这个方法谁调用可以自己查看调用栈。

    init-method、afterPropertiesSet() 的调用

    细心的朋友可能已经发现了在 initializeBean(...) 中有调用到一个叫做 invokeInitMethods(...) 的方法。

    protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
          throws Throwable {
       boolean isInitializingBean = (bean instanceof InitializingBean);
       if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
          if (logger.isTraceEnabled()) {
             logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
          }
          if (System.getSecurityManager() != null) {
             try {
                AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                   ((InitializingBean) bean).afterPropertiesSet();
                   return null;
                }, getAccessControlContext());
             }
             catch (PrivilegedActionException pae) {
                throw pae.getException();
             }
          }
          else {
             ((InitializingBean) bean).afterPropertiesSet();
          }
       }
       if (mbd != null && bean.getClass() != NullBean.class) {
          String initMethodName = mbd.getInitMethodName();
          if (StringUtils.hasLength(initMethodName) &&
                !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                !mbd.isExternallyManagedInitMethod(initMethodName)) {
             invokeCustomInitMethod(beanName, bean, mbd);
          }
       }
    }
    

    BeanDefinition是Bean定义的一个抽象。类似于在Java中存在Class类用于描述一个类,里面有你定义的 Bean 的各种信息。

    在第4行,判断是不是 InitializingBean,如果是的话会进行类型强转,然后调用 afterPropertiesSet()。

    在第26行,获得到自定义初始化方法的名字,然后在第30行调用 invokeCustomInitMethod 执行完成。

    顺序的确定

    protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
       if (System.getSecurityManager() != null) {
          AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
             invokeAwareMethods(beanName, bean);
             return null;
          }, getAccessControlContext());
       }
       else {
          invokeAwareMethods(beanName, bean);
       }
       Object wrappedBean = bean;
       if (mbd == null || !mbd.isSynthetic()) {
          wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
       }
       try {
          invokeInitMethods(beanName, wrappedBean, mbd);
       }
       catch (Throwable ex) {
          throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null),
                beanName, "Invocation of init method failed", ex);
       }
       if (mbd == null || !mbd.isSynthetic()) {
          wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
       }
       return wrappedBean;
    }
    

    initializeBean(...) 方法中,先执行 applyBeanPostProcessorsBeforeInitialization(...) 在执行 invokeInitMethods(...) 。

    而 applyBeanPostProcessorsBeforeInitialization(...) 会执行被 @PostConstruct 标注的方法,invokeInitMethods(...) 会执行 afterPropertiesSet() 和自定义的初始化方法,并且 afterPropertiesSet() 在自定义的初始化方法之前执行。

    所以它们之间的执行顺序是:

    @PostConstruct > afterPropertiesSet() > initMethod()

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持IIS7站长之家博文。