首页 > 编程语言 >Java面试题:@PostConstruct、init-method和afterPropertiesSet执行顺序?

Java面试题:@PostConstruct、init-method和afterPropertiesSet执行顺序?

时间:2024-05-10 12:24:23浏览次数:27  
标签:初始化 面试题 Java mbd PostConstruct bean init null afterPropertiesSet

在Spring框架中,@PostConstruct注解、init-method属性、以及afterPropertiesSet()方法通常用于初始化Bean的逻辑。它们都提供了在Bean创建和初始化完成后执行的方法,但执行顺序有所不同。

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

代码如下:

import org.springframework.beans.factory.InitializingBean;
import javax.annotation.PostConstruct;

public class Foo implements InitializingBean {

    public void init(){
        System.out.println("执行了init生命周期的初始化回调");
    }

    @PostConstruct
    public void postConstruct(){
        System.out.println("执行了postConstruct生命周期的初始化回调");
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println("执行了afterPropertiesSet生命周期的初始化回调");
    }
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class InitConfiguration {
    @Bean(initMethod = "init")
    public Foo getInitMethodBean() {
        return new Foo();
    }
}

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

执行了postConstruct生命周期的初始化回调
执行了afterPropertiesSet生命周期的初始化回调
执行了init生命周期的初始化回调

@PostConstruct是Java EE 5引入的一个注解,它用于标记一个方法,该方法会在依赖注入完成后自动执行。这意味着,一旦Spring容器完成了Bean的实例化和属性赋值,就会调用这个方法。通常,我们会在这个方法中做一些初始化工作,比如启动服务、初始化数据库连接等。

init-method属性是Spring Bean的一个属性,它允许我们指定一个初始化方法。这个方法会在Bean实例化并完成属性注入后自动执行。与@PostConstruct注解不同的是,init-method属性并不依赖于Spring容器,因此可以在没有Spring的环境中运行。

afterPropertiesSet是SpringFramework中的一个初始化方法,它属于 InitializingBean接口的一部分。当bean的所有属性被Spring容器设置之后,这个方法会被自动调用。它允许开发者在bean属性设置完成之后执行一些特定的操作,如数据库连接池的初始化等。这个方法是在执行其他初始化方法之前被调用的。

源码分析:

通过断点调试发现几个初始化方法都定位到AbstractAutowireCapableBeanFactory的initializeBean方法中

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
      AccessController.doPrivileged(new PrivilegedAction<Object>() {
        @Override
        public Object run() {
          invokeAwareMethods(beanName, bean);
          return null;
        }
      }, getAccessControlContext());
    }
    else {
      invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
      // 此处执行的是@PostConstruct注解的方法 InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
      // 执行的是afterPropertiesSet和init-method方法      
      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;
  }

执行afterPropertiesSet和init-method方法,在invokeInitMethods方法里面,如下:

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 (logger.isDebugEnabled()) {
        logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
      }
      if (System.getSecurityManager() != null) {
        try {
          AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
            @Override
            public Object run() throws Exception {
              ((InitializingBean) bean).afterPropertiesSet();
              return null;
            }
          }, getAccessControlContext());
        }
        catch (PrivilegedActionException pae) {
          throw pae.getException();
        }
      }
      else {
        // 执行afterPropertiesSet方法
        ((InitializingBean) bean).afterPropertiesSet();
      }
    }

    if (mbd != null) {
      String initMethodName = mbd.getInitMethodName();
      if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
          !mbd.isExternallyManagedInitMethod(initMethodName)) {
        // 执行自定义的init-method方法
        invokeCustomInitMethod(beanName, bean, mbd);
      }
    }
  }

 

最终的结论是:@PostConstruct > afterPropertiesSet() > initMethod()的顺序

 

往期面试题:

Java面试题:SimpleDateFormat是线程安全的吗?使用时应该注意什么?

Java面试题:细数ThreadLocal大坑,内存泄露本可避免

Java面试题:请谈谈对ThreadLocal的理解?

Java面试题:为什么HashMap不建议使用对象作为Key?

Java面试题:你知道Spring的IOC吗?那么,它为什么这么重要呢?

 

标签:初始化,面试题,Java,mbd,PostConstruct,bean,init,null,afterPropertiesSet
From: https://www.cnblogs.com/marsitman/p/18184050

相关文章

  • Java学设计模式之中介者模式
    一、中介者模式概念1.1什么是中介者模式中介者模式是一种行为型设计模式,它通过将对象之间的交互行为集中到一个中介者对象中来解耦对象之间的关联。这种模式被用来减少对象之间的直接通信,从而降低了系统的耦合度,使得系统易于维护和扩展。结构中介者模式通常包含以下几个要素:......
  • Java 判断是否为工作时间
    /***判断是否为工作时间(上午:7:30-11:30下午:13:30-17:20)*@return*/publicstaticbooleanisWithinWorkingHours(){LocalTimecurrentTime=LocalTime.now();//LocalTimecurrentTime=LocalTime.parse("17:19:59");......
  • 操作系统线程和Java线程的状态
    操作系统线程和Java线程的状态  一、操作系统线程的状态  操作系统的线程主要有以下三个状态  1. 就绪状态(ready):线程正在等待使用CPU,经调度程序调用之后进入running状态。  2.执行状态(running):线程正在使用CPU。  3.等待状态(waiting):线程经过等......
  • 【java】ArrayList和LinkedList的区别
    一、ArrayList和LinkedList的相同点ArrayList和LinkedList都是实现了List接口的容器类,用于存储一系列的对象引用,他们都可以对元素的增删改查进行操作。ArrayList、LinkedList、Vector和Stack是List的四个实现类,List是一个接口,它继承与Collection接口,代表有序的队列。其中Vector......
  • Java学设计模式之享元模式
    一、享元模式概念1.1什么是享元模式享元模式是一种结构型设计模式,旨在通过共享对象来最大程度地减少内存使用和提高性能。在享元模式中,对象被设计成可共享的,以便在需要时能够被多个不同的上下文使用。结构享元模式通常包含以下几个要素:Flyweight(享元):定义了共享对象的接口......
  • JDK源码阅读-------自学笔记(二十六)(java.util.Map 自定义讲解)
    一、简介Map就是用来存储“键(key)-值(value)”对的.通过键寻找value,所以键不能重复.数组的本质也是一种键值对,区别就是索引一般是数字,而Map的Key可以是任意对象(字符串,数字),相当于把数组的索引范围扩的更大,使用更方便.实际开发中较为常用.二、Map的常用方法实例(1......
  • JAVA_WEB复习之请求响应
    简单参数请求:原始的方法,我们需要通过servlet中提供的api,HttpServletRequest(请求对象),获取请求的相关信息。比如获取请求参数:当tomcat接收到请求时,它会把请求的信息封装httpservletrequest到对象中。而在Springboot的环境,原始的API进行了封装,接收参数的形式更加简单。如果是简单......
  • Springboot项目的jar包的运行方式以及使用yum安装java后忘记了位置
    SpringBoot项目打包后的jar的部署方式这里我写了五种部署方式1.直接启动java-jarxxx.jar这种方式就只适合自己在测试时用一下,关闭会话就能停止运行属实是方便。2.后台启动java-jarxxx.jar&在后台静默启动,同样关闭会话也会停止,优点是和上面一样,日志是打印在窗口的3......
  • java9
    小练习:publicclasstest1{publicstaticvoidmain(String[]args){Scannersc=newScanner(System.in);System.out.println("请输入当前机票的原价");intticket=sc.nextInt();System.out.println("请输入月份");int......
  • Java根据数学公式计算出结果
    importjavax.script.ScriptEngine;importjavax.script.ScriptEngineManager;importjavax.script.ScriptException;publicclassTest{publicstaticvoidmain(String[]args){//年均充放电量=(E2*H4*G4*0.95*0.86)*0.871/10000Stringexpressi......