面试题-Spring Bean的生命周期

news2024/9/19 10:13:30

文章目录

  • Spring Bean 生命周期分为哪几个阶段
  • 浅析Bean生命周期源码实现
    • 1.1 DefaultListableBeanFactory
    • 1.2 createBean
    • 2.1 populateBean
    • 3.1 initializeBean
    • 3.2 invokeInitMethod
    • 3.3 applyBeanPostProcessorsBeforeInitialization
    • 5.1 destroyBean
    • 5.2 invokeDestroyMethod
  • Spring Bean的后置处理器是什么?在项目中如何使用它?
  • Spring Bean的生命周期中,哪些阶段可以介入自定义操作
    • 初始化前
    • 初始化时
    • 初始化后
    • 销毁前
  • Spring Bean 的作用域都有哪些?如何控制 Bean 的生命周期?
  • Spring中的延迟初始化是什么,如何配置延迟初始化?

Spring Bean 生命周期分为哪几个阶段

在这里插入图片描述

  1. 实例化:Spring 容器通过调用构造函数来创建 Bean 的实例。在这个阶段,Spring 还会根据配置文件或者注解来决定 Bean 的具体实现类。

  2. 填充属性:在 Bean 实例化之后,Spring 会将配置文件中定义的属性注入到 Bean 实例中。这些属性可以是简单的数据类型,也可以是其他 Bean 的引用,具体取决于 Bean 的定义和注解配置。

  3. 初始化

    • @PostConstruct 注解的方法:如果 Bean 类中有使用 @PostConstruct 注解的方法,Spring 会在填充属性之后调用这些方法。这个阶段通常用于 Bean 的初始化逻辑。
    • InitializingBean 接口:如果 Bean 实现了 InitializingBean 接口,Spring 会调用 afterPropertiesSet() 方法来进行初始化。
    • init-method 配置:在 XML 配置中,如果定义了 init-method,Spring 会在填充属性之后调用指定的方法进行初始化。
  4. 使用:在 Bean 完成初始化之后,Spring 容器就可以将其交给应用程序使用了。这个阶段是 Bean 的正常工作阶段。

  5. 销毁

    • @PreDestroy 注解的方法:如果 Bean 类中有使用 @PreDestroy 注解的方法,Spring 会在 Bean 销毁之前调用这些方法。这个阶段用于进行 Bean 的清理工作。
    • DisposableBean 接口:如果 Bean 实现了 DisposableBean 接口,Spring 会调用 destroy() 方法来进行销毁操作。
    • destroy-method 配置:在 XML 配置中,如果定义了 destroy-method,Spring 会在 Bean 销毁之前调用指定的方法进行清理。

Bean详细生命周期过程:
在这里插入图片描述


浅析Bean生命周期源码实现

以下是精简代码:

1.1 DefaultListableBeanFactory

DefaultListableBeanFactory是Spring的核心BeanFactory实现,它负责Bean的创建和管理。在这个类中,Bean实例化的过程主要通过getBean方法来触发。

public <T> T getBean(Class<T> requiredType) throws BeansException {
    return doGetBean(null, null, requiredType, false);
}

public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
}

在doGetBean方法中,Spring会检查Bean是否已经存在于缓存中。如果不存在,则会调用createBean方法进行实例化。

protected <T> T doGetBean(String name, Class<T> requiredType, Object[] args, boolean typeCheckOnly) {
    // 省略部分逻辑
    if (bean != null) {
        return (T) bean;
    }
    return (T) createBean(beanName, mbd, args);
}

1.2 createBean

createBean方法负责Bean的实际实例化过程。首先,Spring会使用反射创建Bean实例。

protected <T> T createBean(String beanName, RootBeanDefinition mbd, Object[] args) {
    // 省略部分逻辑
    BeanWrapper beanWrapper = createBeanWrapper(beanClass);
    Object beanInstance = beanWrapper.getWrappedInstance();
    return (T) beanInstance;
}

在这个方法中,createBeanWrapper会创建一个BeanWrapper实例,用于包装和操作Bean实例。

protected BeanWrapper createBeanWrapper(Object bean) {
    return new BeanWrapperImpl(bean);
}

2.1 populateBean

populateBean方法负责将Bean的依赖注入到实例中。它会读取BeanDefinition中的属性配置,并将这些属性值注入到Bean实例中。

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
    PropertyValues pvs = mbd.getResolvedProps();
    if (pvs != null) {
        for (PropertyValue pv : pvs.getPropertyValues()) {
            bw.setPropertyValue(pv.getName(), pv.getValue());
        }
    }
}

3.1 initializeBean

初始化阶段主要由 AbstractAutowireCapableBeanFactory 类中的 initializeBean 方法负责。这个方法会在 DefaultListableBeanFactorycreateBean 方法中被调用。

在这个方法中,首先检查Bean是否实现了InitializingBean接口。如果实现了,则调用其afterPropertiesSet方法。afterPropertiesSet方法中可以包含一些自定义的初始化逻辑,例如检查必需的属性是否被正确设置等。

protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {
    // 调用 InitializingBean 的 afterPropertiesSet 方法
    if (bean instanceof InitializingBean) {
        try {
            ((InitializingBean) bean).afterPropertiesSet();
        } catch (Exception ex) {
            throw new BeanInitializationException("Initialization of bean failed", ex);
        }
    }

    // 调用用户自定义的初始化方法
    String initMethodName = mbd.getInitMethodName();
    if (initMethodName != null) {
        invokeInitMethod(beanName, bean, mbd);
    }

    return bean;
}

3.2 invokeInitMethod

invokeInitMethod方法会通过反射调用用户定义的初始化方法。这些方法是通过RootBeanDefinition中的initMethodName属性指定的。

private void invokeInitMethod(String beanName, Object bean, RootBeanDefinition mbd) {
    String initMethodName = mbd.getInitMethodName();
    try {
        Method initMethod = bean.getClass().getMethod(initMethodName);
        initMethod.invoke(bean);
    } catch (Exception e) {
        throw new BeansException("Failed to invoke init method '" + initMethodName + "' on bean with name '" + beanName + "'", e);
    }
}

invokeInitMethod方法会通过反射查找并调用指定的初始化方法。这个方法通常用于执行一些特定的初始化操作,比如建立数据库连接等

3.3 applyBeanPostProcessorsBeforeInitialization

Spring会在Bean的初始化过程中扫描并执行@PostConstruct注解标记的方法。这一过程通常发生在AbstractAutowireCapableBeanFactory类的applyBeanPostProcessorsBeforeInitialization方法中。

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

在invokeInitMethod方法之前,会调用@PostConstruct注解的方法。
在这里插入图片描述

5.1 destroyBean

destroyBean 方法在 Spring 的 AbstractAutowireCapableBeanFactory 类中定义。它的作用是执行 Bean 销毁前的处理,包括调用 DisposableBean 接口的 destroy 方法和用户自定义的销毁方法。

public void destroyBean(String beanName, Object bean) {
    // 调用DisposableBean的destroy方法
    if (bean instanceof DisposableBean) {
        try {
            ((DisposableBean) bean).destroy();
        } catch (Exception e) {
            throw new BeansException("Failed to invoke destroy method on bean with name '" + beanName + "'", e);
        }
    }
    
    // 调用用户自定义销毁方法
    String destroyMethodName = mbd.getDestroyMethodName();
    if (destroyMethodName != null) {
        invokeDestroyMethod(beanName, bean, mbd);
    }
}

5.2 invokeDestroyMethod

invokeDestroyMethod方法通过反射调用用户定义的销毁方法。

private void invokeDestroyMethod(String beanName, Object bean, String destroyMethodName) {
    try {
        Method destroyMethod = bean.getClass().getMethod(destroyMethodName);
        destroyMethod.invoke(bean);
    } catch (Exception ex) {
        throw new BeanCreationException("Failed to invoke destroy method '" + destroyMethodName + "' on bean with name '" + beanName + "'", ex);
    }
}

Spring Bean的后置处理器是什么?在项目中如何使用它?

Spring Bean 的后置处理器(BeanPostProcessor)是 Spring 框架中的一种机制,用于在 Bean 实例化和初始化的过程中插入自定义逻辑。它允许开发者在 Bean 实例化后、初始化前,和初始化后,执行额外的处理。

使用 BeanPostProcessor
要使用 BeanPostProcessor,需要实现 org.springframework.beans.BeanPostProcessor 接口,并重写 postProcessBeforeInitializationpostProcessAfterInitialization 方法。以下是如何在项目中使用它的步骤:

  1. 创建自定义的 BeanPostProcessor 实现:

    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    
    public class CustomBeanPostProcessor implements BeanPostProcessor {
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 在 Bean 初始化之前进行处理
        System.out.println("Before Initialization: " + beanName);
        return bean; // 返回处理后的 Bean
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 在 Bean 初始化之后进行处理
        System.out.println("After Initialization: " + beanName);
        return bean; // 返回处理后的 Bean
    }
    
  2. 将自定义 BeanPostProcessor 注册到 Spring 容器中:

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class AppConfig {
    	@Bean
    	public CustomBeanPostProcessor customBeanPostProcessor() {
        	return new CustomBeanPostProcessor();
    	}
    }
    

应用场景

  • 动态修改 Bean:在 Bean 初始化后对其进行修改,例如为 Bean 添加额外的属性或功能。
  • 日志记录:记录 Bean 的创建和初始化过程,便于调试和监控。
    创建代理:使用 AOP 创建 Bean 的代理对象,以添加额外的功能如事务管理、安全控制等。

Spring Bean的生命周期中,哪些阶段可以介入自定义操作

初始化前

方法:

  • postProcessBeforeInitialization(由 BeanPostProcessor 提供)

应用: 在Bean初始化之前执行额外的处理,例如修改Bean的状态或属性,进行预处理操作。

初始化时

方法:

  • afterPropertiesSet(由 InitializingBean 接口提供)
  • @PostConstruct 注解方法
  • XML 配置中的 init-method 方法

应用: 在Bean的属性设置完成后,执行初始化逻辑,如设置默认值、进行资源准备或配置。

初始化后

方法:

  • postProcessAfterInitialization(由 BeanPostProcessor 提供)

应用: 在Bean初始化之后执行附加处理,例如创建代理对象、添加功能、日志记录等。

销毁前

方法:

  • destroy(由 DisposableBean 接口提供)
  • @PreDestroy 注解方法
  • XML 配置中的 destroy-method 方法

应用: 在Bean销毁之前进行清理操作,例如释放资源、保存状态或关闭连接等。


Spring Bean 的作用域都有哪些?如何控制 Bean 的生命周期?

  1. Singleton(单例)
    描述: 默认作用域。Spring容器中只创建一个Bean实例,并在整个容器中共享这个实例。
    生命周期: 从容器启动到容器关闭期间,Bean实例始终存在。
  2. Prototype(原型)
    描述: 每次请求都会创建一个新的Bean实例。每个实例都有独立的生命周期。
    生命周期: 每次获取Bean时,都会创建一个新的实例。Bean的生命周期从创建开始,到被垃圾回收时结束。
  3. Request(请求)
    描述: 每个HTTP请求都会创建一个新的Bean实例。Bean的生命周期与HTTP请求相对应。
    生命周期: 从每个HTTP请求开始到请求结束,每次请求都会创建一个新的Bean实例。
  4. Session(会话)
    描述: 每个HTTP会话创建一个新的Bean实例。Bean的生命周期与HTTP会话相对应。
    生命周期: 从每个HTTP会话开始到会话结束,每个会话都有一个独立的Bean实例。
  5. GlobalSession(全局会话)
    描述: 在Portlet环境中使用。每个全局会话创建一个新的Bean实例。
    生命周期: 适用于Portlet应用中的全局会话,每个全局会话有一个独立的Bean实例。

例如,配置一个原型作用域的 Bean:

<bean id="myBean" class="com.example.MyBean" scope="prototype"/>

或通过注解@scope 设置

此外,我们还可以自定义 Bean 的初始化和销毁方法,以在 Bean 作用域开始和结束时执行特定逻辑。可以通过 init-methoddestroy-method 属性来指定这些方法:

<bean id="myBean" class="com.example.MyBean" scope="prototype"
      init-method="start" destroy-method="end">
</bean>

Spring中的延迟初始化是什么,如何配置延迟初始化?

在Spring中,延迟初始化(Lazy Initialization)是一种延迟Bean创建的机制,即只有在实际需要Bean时才会创建它,而不是在容器启动时就立即创建。这可以提高应用程序的启动速度和资源利用率,特别是对于那些不常用的Bean。

延迟初始化的配置方式

  1. 通过 @Lazy 注解

    • 应用场景:在类级别或方法级别使用,表示当Bean首次被使用时才会被创建。
    • 配置方式:
      • 在类上:对整个Bean类应用延迟初始化。
      @Lazy
      @Component
      public class MyBean {
          // Bean实现
      }
      
      • 在方法上:对特定的Bean方法应用延迟初始化。
      @Configuration
      public class AppConfig {
          
          @Bean
          @Lazy
          public MyBean myBean() {
              return new MyBean();
          }
      }
      
  2. 在 XML 配置中

    • 应用场景:在传统的XML配置中配置延迟初始化。
    • 配置方式:
    <bean id="myBean" class="com.example.MyBean" lazy-init="true"/>
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2044899.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python爬虫——爬取某网站的视频

爬取视频 本次爬取&#xff0c;还是运用的是requests方法 首先进入bilibili官网中&#xff0c;选取你想要爬取的视频&#xff0c;进入视频播放页面&#xff0c;按F12&#xff0c;将网络中的名称栏向上拉找到第一个并点击&#xff0c;可以在标头中&#xff0c;找到后续我们想要…

一次评审会议上的纠偏

这段时间&#xff0c;整个项目组都投入在某个专项项目中&#xff0c;评审和版本迭代的频率也很高。而在近期的评审会上&#xff0c;发生了一起激烈的争辩&#xff0c;也让我意识到大多数产品人身上的通病&#xff0c;觉得挺有意义的&#xff0c;借此分享给大家。 同事A最近在做…

Qt窗口交互场景、子窗口数据获取

一、前言 在现代软件开发中&#xff0c;图形用户界面&#xff08;GUI&#xff09;的设计不仅仅关乎美观&#xff0c;更在于用户体验和功能的无缝衔接。Qt框架以其强大的跨平台能力和丰富的组件库&#xff0c;成为众多开发者构建GUI应用的首选工具。在Qt应用中&#xff0c;窗口…

​【经验分享】微机原理、指令判断、判断指令是否正确判断指令是否正确​

目录 微机原理判断指令是否正确【见的多了&#xff0c;你就懂了~】 1. 立即数不能作为目标操作数 2. 操作数位数必须匹配 3. 需要指定存储器操作数的字节或字 4. 两个操作数不能同时为存储器操作数 5. 循环次数超过1必须使用CL寄存器 6. 段寄存器限制&#xff08;特别是…

比OpenAI的Whisper快50%,最新开源语音模型

生成式AI初创公司aiOla在官网开源了最新语音模型Whisper-Medusa&#xff0c;推理效率比OpenAI开源的Whisper快50%。 aiOla在Whisper的架构之上进行了修改采用了“多头注意力”机制的并行计算方法&#xff0c;允许模型在每个推理步骤中预测多个token&#xff0c;同时不会损失性…

[000-01-010].第02节:Spring基础开发环境搭建

1.1.新建空项目&#xff1a; 1.新建Empty项目&#xff0c;主要是为了方便之后把各个模块的代码统一的放在一起&#xff1a; 2.设置JDK&#xff1a; 3.设置maven版本&#xff1a; 1.2.建立第一个Spring项目模块&#xff1a; 1.新建模块&#xff1a; 2.配置依赖&#xff…

gitlab自动部署是什么 gitlab自动部署如何进行操作

在现代软件开发流程中&#xff0c;自动化部署是提高效率和确保软件质量的关键环节。GitLab作为一个强大的DevOps平台&#xff0c;提供了完整的自动部署工具&#xff0c;帮助开发团队实现代码从编写到生产的无缝转换。本文将详细解析GitLab的自动部署功能是什么&#xff0c;如何…

走向绿色:能源新选择,未来更美好

当前&#xff0c;全球范围内可再生能源正经历着从辅助能源向核心能源的深刻转型&#xff0c;绿色能源日益渗透至居住、出行、日常应用等多个领域&#xff0c;深刻影响着我们的生活方式&#xff0c;使我们能够更加充分地体验清洁能源所带来的优质生活。 一、绿色能源与“住” …

Fluent学习笔记——催化转化器内流场仿真(含多孔介质)

参考课程&#xff1a; 标题&#xff1a;【ANSYS Fluent教程|流体仿真基础入门105讲&#xff08;官方最新案例讲解&#xff09;】 作者&#xff1a;仿真秀APP 选集&#xff1a;P35-P40https://www.bilibili.com/video/BV1vT4y1z7on?p35&vd_source7e977d0187273d77005659cdd…

数据结构(03):线性表的逻辑结构

线性表的逻辑结构 在谈论逻辑结构的时候&#xff0c;我们不讨论具体数据在物理内存中的存储细节&#xff0c;而只关注线性表的逻辑结构。数据结构系列文章02介绍过常见的逻辑结构有&#xff1a;集合、线性表结构、树结构和图结构等。 A.What&#xff08;什么是线性表&#xff0…

什么是凤凰雪球期权?和雪球期权有什么区别?

凤凰结构&#xff0c;和经典雪球结构类似&#xff0c;属于障碍期权的一种。凤凰结构中包括敲入事件&#xff0c;也包括敲出事件&#xff0c;最后的收益取决于挂钩标的走势和敲入、敲出事件发生的时间&#xff0c;不过在收益计算规则上与雪球有所不同&#xff0c;下文为大家科普…

deepin V23 Release 安装与功能介绍!!!

原文链接&#xff1a;deepin V23 Release 安装与功能介绍&#xff01;&#xff01;&#xff01; Hello&#xff0c;大家好啊&#xff01;2024年8月15日&#xff0c;deepin正式发布了最新版操作系统——deepin V23。今天&#xff0c;我将为大家带来一篇关于deepin V23的安装及功…

python完整爬取工商数据代码实例+数据展示

在数据爬虫这个领域&#xff0c;我早已不再是新手&#xff0c;而是一位经验丰富的老手。长期以来&#xff0c;我通过不断实践和探索&#xff0c;掌握了一系列高效的爬虫技术&#xff0c;特别是在应对复杂网页和动态数据时&#xff0c;更是积累了独特的经验。 初识爬虫&#xf…

SAP BRIM用于应收账款AR收入中台

SAP BRIM&#xff08;Billing and Revenue Innovation Management&#xff09;是SAP提供的一个综合性解决方案&#xff0c;旨在帮助企业高效管理计费和收入流程。它与SAP ERP系统集成&#xff0c;提供端到端的功能&#xff0c;简化计费流程&#xff0c;自动化收入确认&#xff…

【云原生】Ingress控制器超级详解

Ingress资源对象 文章目录 Ingress资源对象一、Ingress1.1、Ingress是什么&#xff1f;1.2、Ingress术语1.3、Ingress类型 二、Ingress详细2.1、部署Nginx-Ingress控制器2.2、最小Ingress资源2.3、Ingress规则 三、一个域名多个访问路径多SVC四、多域名Ingress五、转发到默认Se…

基于Java+SpringBoot+Vue的网上购物商城系统研发

基于JavaSpringBootVue的网上购物商城系统研发 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f3…

排序算法【希尔排序】

一、原理 &#xff08;1&#xff09;步长为4时候的插入排序 &#xff08;2&#xff09;步长为2的时候的插入排序 &#xff08;3&#xff09;步长为1的时候的插入排序 二、代码如下所示&#xff1a; #ifndef __TEST_H__ #define __TEST_H__ #include <string.h> #include…

【IEEE出版,华中科技大学主办】2024年电气工程自动化与信息处理国际会议(EEAIP 2024,9月20-22)

2024年电气工程自动化与信息处理国际会议&#xff08;EEAIP 2024&#xff09;作为第四届能源、动力与电气工程国际学术会议&#xff08;EPEE 2024&#xff09;的分会场&#xff0c;将于2024年9月20-22日在中国武汉举行。 本届主会&#xff08;EPEE 2024&#xff09;由华中科技大…

【Python - 爬虫】SSLError 错误:处理 HTTPS 请求中的 SSLError 问题

文章目录 一、SSLError 错误概述1. 错误的产生2. 常见的错误信息 二、SSLError 的可能原因分析1. 代理配置问题2. 网络传输问题3. 证书问题 三、解决方案与最佳实践1. 配置代理2. 通过系统代理3. 使用 requests.Session 管理请求4. 临时禁用代理 四、其他解决方法1. 更新证书2.…

4.4 数据查询语言(DQL):复杂查询与连接操作

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; 工&#x1f497;重&#x1f497;hao&#x1f497;&#xff1a;野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题.…