spring重点面试题总结

bean的生命周期

在 Spring 中,BeanDefinition、Bean 实例化、依赖注入、Aware 接口的处理、以及 BeanPostProcessor 的前置和后置处理等,都是 Spring 容器管理 Bean 生命周期的关键部分。下面我将详细解释这些过程。

1. 通过 BeanDefinition 获取 Bean 的定义信息

BeanDefinition 是 Spring 容器中用于描述 Bean 的元数据的接口,包含了 Bean 的配置和信息,如 Bean 的类名、构造参数、属性等。通过 BeanDefinition,Spring 容器可以知道如何实例化 Bean。

  • 获取 BeanDefinition 示例
    Spring 容器在启动时,会加载并解析所有的配置(如 XML 配置文件、注解配置、Java 配置类等),将每个 Bean 的配置存储在 BeanDefinition 中。

    BeanFactory beanFactory = new AnnotationConfigApplicationContext(AppConfig.class);
    BeanDefinition beanDefinition = beanFactory.getBeanDefinition("myBean");
    String beanClassName = beanDefinition.getBeanClassName();
    
  • BeanDefinition的常见方法

    • getBeanClassName():获取 Bean 的类名。
    • getPropertyValues():获取 Bean 的属性值。
    • getConstructorArgumentValues():获取 Bean 的构造方法参数。

2. 调用构造函数实例化 Bean

Spring 通过反射调用构造函数来实例化 Bean。在实例化过程中,Spring 会选择合适的构造器,并根据配置提供的构造参数来实例化对象。

  • 步骤
    1. Spring 通过 BeanDefinition 中的类名和构造方法配置,选择一个构造方法。
    2. 如果有构造方法参数(例如在 XML 配置中或注解中配置了构造器注入),Spring 会从配置中获取并注入这些参数。
    3. 使用反射调用构造函数创建 Bean 实例。

例如:

@Bean
public MyBean myBean() {return new MyBean("arg1", 2);
}

3. Bean 的依赖注入

依赖注入是 Spring 中实现控制反转(IoC)的核心机制。Spring 会根据配置将 Bean 的依赖自动注入到 Bean 中。

  • 依赖注入的方式

    • 构造器注入:通过 Bean 的构造方法传入依赖。
    • Setter 方法注入:通过设置属性的 setter 方法传入依赖。
    • 字段注入:通过反射直接注入字段。
  • Spring 如何注入依赖

    • 对于构造器注入,Spring 会根据 @Autowired 注解或 XML 配置来自动匹配依赖的类型。
    • 对于字段注入和 setter 注入,Spring 会将依赖通过反射注入到 Bean 中。

4. 处理 Aware 接口(BeanNameAwareBeanFactoryAwareApplicationContextAware

Spring 提供了一些接口,让 Bean 在初始化过程中能够访问到容器的相关信息。通过实现这些接口,Bean 可以获得更多的容器信息,如其自身的 Bean 名称、BeanFactory 或 ApplicationContext。

  • BeanNameAware:通过 setBeanName() 方法获取当前 Bean 在容器中的名称。

    public class MyBean implements BeanNameAware {@Overridepublic void setBeanName(String name) {System.out.println("Bean name is: " + name);}
    }
    
  • BeanFactoryAware:通过 setBeanFactory() 方法获得当前 BeanFactory 实例,可以访问容器中的其他 Bean。

    public class MyBean implements BeanFactoryAware {@Overridepublic void setBeanFactory(BeanFactory beanFactory) {System.out.println("BeanFactory is: " + beanFactory);}
    }
    
  • ApplicationContextAware:通过 setApplicationContext() 方法获得当前 ApplicationContext 实例,从而能够访问 Spring 环境和其他 Bean。

    public class MyBean implements ApplicationContextAware {@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println("ApplicationContext is: " + applicationContext);}
    }
    

5. Bean 的后置处理器(BeanPostProcessor)- 前置处理

BeanPostProcessor 是一个接口,允许在 Bean 初始化的前后对 Bean 进行修改。Spring 会自动检测容器中实现了 BeanPostProcessor 接口的类,并在每个 Bean 初始化之前和之后调用相应的方法。

  • postProcessBeforeInitialization:在 Bean 初始化之前调用,允许你修改 Bean 的属性或执行一些额外的操作。

    public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("Before initialization: " + beanName);return bean;  // 返回修改后的 Bean 对象}
    }
    

    这里,postProcessBeforeInitialization 方法在 Bean 的初始化方法(如 @PostConstructafterPropertiesSet())之前被调用。

6. 初始化方法(InitializingBeaninit-method

  • InitializingBean 接口afterPropertiesSet() 方法会在所有的属性都设置完毕后调用,通常用于进行 Bean 的初始化操作。

    public class MyBean implements InitializingBean {@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("Bean has been initialized");}
    }
    
  • @PostConstruct 注解:这个注解标记的方法会在所有的依赖注入完成之后、Bean 初始化之前调用。

    public class MyBean {@PostConstructpublic void init() {System.out.println("Bean initialized using @PostConstruct");}
    }
    
  • 自定义初始化方法:在 XML 配置文件中,或者 Java 配置类中,可以通过 init-method 配置一个自定义的初始化方法。

    <bean id="myBean" class="com.example.MyBean" init-method="init">...
    </bean>
    
    @Bean(initMethod = "init")
    public MyBean myBean() {return new MyBean();
    }
    

7. Bean 的后置处理器(BeanPostProcessor)- 后置处理

BeanPostProcessor 接口还提供了 postProcessAfterInitialization 方法,该方法在 Bean 初始化完成后调用。这个方法可以用于对已经初始化的 Bean 进行修改或增强(如 AOP)。

  • postProcessAfterInitialization:在 Bean 初始化之后调用,通常用于处理 Bean 的代理或日志功能等。

    public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("After initialization: " + beanName);return bean;  // 返回修改后的 Bean 对象}
    }
    

8. 使用(Usage)

  • 发生时机:初始化完成后,Spring Bean 就可以进入应用程序的使用阶段。

  • 具体操作

    • 这时,Spring Bean 已经是完整的,可以被应用程序中的其他组件或类通过 @Autowired 自动注入、手动查找或通过其它方式使用。
    • Bean 可以被正常使用,比如进行方法调用或响应 HTTP 请求等。
  • 关键点

    • 在这个阶段,Bean 已经完全初始化,所有的依赖都已注入,并且初始化方法也已执行。

9. 销毁前处理(Post-process Before Destruction)

  • 发生时机:在容器销毁之前,Spring 会进行销毁前的处理。

  • 具体操作

    • 如果 Bean 实现了 DisposableBean 接口,Spring 会调用 destroy() 方法。
    • 如果 Bean 使用了 @PreDestroy 注解,Spring 会在销毁之前调用该方法。
    • 如果 Bean 配置了自定义销毁方法(在 XML 配置中通过 destroy-method 或者 Java 配置类中的 @Bean(destroyMethod = "...")),Spring 会调用该方法。
  • 关键点

    • 这个阶段主要是进行资源的释放工作,比如关闭文件流、数据库连接、清理缓存等操作。

10. 销毁(Destruction)

  • 发生时机:Spring 容器关闭时,销毁所有的 Bean。

  • 具体操作

    • Spring 会在容器销毁之前,销毁所有的 Bean。这个过程包括:
      • 释放所有与 Bean 相关的资源。
      • 如果 Bean 是 singleton(单例模式),那么销毁时会调用销毁方法。
      • 对于 prototype(原型模式)Bean,Spring 不负责销毁,通常需要开发者手动销毁。
  • 关键点

    • 容器关闭时,Spring 会销毁所有在容器中管理的 Bean,并释放相应的资源。特别是对于单例 Bean,销毁时会执行注册的销毁方法。

生命周期钩子接口和注解

  • InitializingBeanDisposableBean:这两个接口提供了回调方法(afterPropertiesSet()destroy())来进行初始化和销毁的自定义操作。
  • @PostConstruct@PreDestroy:Java 注解,分别用于定义初始化和销毁方法。
  • BeanPostProcessor:可以在 Bean 初始化前后进行处理,常用于 AOP 或修改 Bean 实例。

通过这些不同的阶段和钩子方法,Spring 提供了非常灵活的 Bean 生命周期管理机制,允许开发者定制 Bean 的行为。


总结

Spring 的 Bean 生命周期包括从 Bean 的定义、实例化、依赖注入,到初始化、销毁等多个阶段。在这个过程中,BeanDefinition 提供了 Bean 的配置信息,Spring 会通过反射实例化 Bean,并通过注入不同的依赖来完成 Bean 的构建。BeanPostProcessor 在 Bean 初始化前后提供了扩展点,允许开发者进行自定义的 Bean 修改和增强。同时,通过实现 Aware 接口,Bean 可以获取到容器的一些元数据,从而进一步增强 Bean 的功能。



什么是 Spring 的循环依赖问题?

定义
  • 循环依赖指的是两个或多个 Bean 之间互相依赖,形成一个依赖环。例如:
    • Bean A 依赖 Bean B,而 Bean B 又依赖 Bean A。
    • 更复杂的情况是多个 Bean 相互嵌套依赖,比如 A → B → C → A。
问题出现的场景
  • 在 Spring 容器中,Bean 的实例化、初始化是分步骤进行的。如果 Bean 需要其依赖的对象,但该对象还未完成创建,就会导致循环依赖问题。

2. Spring Bean 的创建流程

  1. 实例化 (Instantiation): 创建 Bean 的原始对象(通过构造器或工厂方法)。
  2. 属性注入 (Populate Properties): 将依赖的其他 Bean 注入到当前 Bean。
  3. 初始化 (Initialization): 执行 @PostConstruct 方法、InitializingBean 的回调等初始化逻辑。
  4. 完成 Bean 创建: Bean 准备好供其他对象使用。

问题出现点:

  • 如果两个 Bean 在属性注入阶段互相依赖,Spring 会因依赖未完成而报错。

3. 循环依赖的分类

  1. 构造器循环依赖:

    • 两个或多个 Bean 的构造函数中互相依赖。
    • 例如:
      @Component
      public class A {public A(B b) {}
      }@Component
      public class B {public B(A a) {}
      }
      
    • 特点: Spring 无法解决,直接报错,因为在实例化阶段就需要依赖,Spring 没法提前暴露 Bean。
  2. 字段/Setter 循环依赖:

    • 两个或多个 Bean 通过字段或 Setter 方法互相依赖。
    • 例如:
      @Component
      public class A {@Autowiredprivate B b;
      }@Component
      public class B {@Autowiredprivate A a;
      }
      
    • 特点: Spring 可以通过 三级缓存 解决。

4. Spring 的解决办法

4.1. 三级缓存机制

Spring 通过三级缓存机制解决 字段/Setter 循环依赖

  1. 单例池(一级缓存):

    • 保存已经完成初始化的单例 Bean。
    • singletonObjects 映射:{beanName: beanInstance}
  2. 早期曝光的单例 Bean(二级缓存):

    • 保存半成品(实例化但未初始化完成的 Bean)。
    • earlySingletonObjects 映射:{beanName: earlyBeanInstance}
  3. 单例工厂(三级缓存):

    • 保存一个工厂方法,用于生成早期 Bean。
    • singletonFactories 映射:{beanName: ObjectFactory}

4.2. 三级缓存的工作流程
  1. 实例化阶段:

    • 创建 Bean 的实例,但未注入依赖。
    • 将工厂方法(ObjectFactory)放入三级缓存 singletonFactories 中。
  2. 属性注入阶段:

    • 如果 Bean 依赖另一个 Bean,而该 Bean 还未完全初始化:
      • 从一级缓存中尝试获取。
      • 如果一级缓存未命中,从二级缓存中获取。
      • 如果二级缓存仍未命中,从三级缓存中获取,并通过工厂方法生成早期 Bean,随后将其移动到二级缓存。
  3. 初始化完成后:

    • 将完全初始化的 Bean 从二级缓存移动到一级缓存,并从其他缓存中移除。

4.3. 为什么需要三级缓存?
  • 问题一:只有一级缓存时

    • 只能存放完全初始化完成的 Bean,对于循环依赖无法处理,因为依赖对象未初始化完成时无法注入。
  • 问题二:只有二级缓存时

    • 二级缓存直接存储早期 Bean 实例,但没有工厂方法来生成代理对象。如果 Bean 是 代理对象(比如使用 AOP),无法提前暴露真实的代理对象。
  • 三级缓存的作用

    • 在实例化阶段,允许通过工厂方法生成代理对象,从而解决 Bean 的代理问题(如 @Transactional@Async)。
    • 工厂方法可以控制生成的是原始对象还是代理对象,再将其放入二级缓存。

5. 示例代码

循环依赖示例
@Component
public class A {@Autowiredprivate B b;public A() {System.out.println("A is created");}public void doSomething() {System.out.println("A is working with B");}
}@Component
public class B {@Autowiredprivate A a;public B() {System.out.println("B is created");}public void doSomething() {System.out.println("B is working with A");}
}
运行输出
A is created
B is created
A is working with B
B is working with A
解析
  1. Spring 首先实例化 Bean A,但在注入 B 时发现 B 未完成。
  2. Spring 实例化 Bean B,发现 A 尚未完成,将早期的 A 暴露到三级缓存中。
  3. Spring 从三级缓存获取早期的 A,完成 B 的依赖注入。
  4. 最终完成 AB 的初始化。

6. 总结

  1. 循环依赖类型:

    • 构造器循环依赖:无法解决。
    • 字段/Setter 循环依赖:通过三级缓存解决。
  2. 三级缓存的必要性:

    • 一级缓存存放完整 Bean。
    • 二级缓存存放早期 Bean,但无法处理代理对象问题。
    • 三级缓存通过工厂方法解决代理 Bean 的暴露问题。
  3. 原理优势:

    • 三级缓存确保 Spring 能在复杂场景下正确地解析和初始化循环依赖的 Bean,同时支持代理对象和增强功能。

@lazy可以解决构造器循环依赖

1. @Lazy 的作用

当一个 Bean 使用 @Lazy 注解时,Spring 容器并不会在应用启动时立即实例化它,而是等到该 Bean 第一次被使用时才进行实例化。这对于减少应用启动时的初始化时间或者避免不必要的资源消耗非常有帮助。

具体来说,@Lazy 可以用于:

  • 懒加载单例 Bean:使某个单例 Bean 在第一次使用时才实例化。
  • 懒加载依赖:使 Bean 的依赖对象在实际需要时才被创建,而不是在 Bean 本身被实例化时就创建。

2. @Lazy 的工作原理

@Lazy 的工作原理基于 Spring 的 延迟初始化(lazy initialization)机制。具体来说,它是通过 代理机制 来实现懒加载的。当一个 Bean 被标记为 @Lazy 时,Spring 会为该 Bean 创建一个代理对象,该代理对象会在第一次被访问时才创建实际的 Bean 实例。

3. @Lazy 的实现

  • 懒加载的代理:当你将一个 Bean 设置为懒加载时,Spring 会使用代理来实现懒加载。在大多数情况下,Spring 会使用 CGLIBJDK 动态代理 创建代理对象。这个代理对象会在你第一次访问 Bean 时才初始化实际的 Bean 实例,并将其代理对象替换为真正的 Bean 实例。

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

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

相关文章

【文件操作】写入json文件

一般用json.dump把dict或者list导出到文件 一般用json.dumps把dict或者list导出到字符串 dump是导出的意思,这样看dict和list是自己人,但是string和文件相对于代码就是外人 代码例子 import json import copyfid = open(origin.txt, r) lines = fid.readlines() res_list…

单片机的选择因素

在选择单片机型号时&#xff0c;需要根据具体的应用需求来选择合适的单片机。单片机&#xff08;Microcontroller Unit, MCU&#xff09;是一种将计算机的主要部分集成在一个芯片上的微型计算机&#xff0c;它通常包括处理器、存储器、输入/输出接口等。随着技术的发展&#xf…

使用wol-web通过NAS远程唤醒电脑

相信很多人都有远程开机的需求&#xff0c;有时候需要远程控制家中的电脑办公、玩游戏。远程控制比较好实现&#xff0c;安装类似向日葵的软件即可。而远程开机稍微麻烦一点&#xff0c;有的通过物理开关实现&#xff08;开机棒&#xff09;我们选择远程网络唤醒的方式实现&…

TortoiseGit 图标覆盖设置

TortoiseGit 图标覆盖设置 图标覆盖设置隐藏图标覆盖切换样式 我们安装了小海龟后&#xff0c;它会在仓库目录下给所有图标覆盖上状态标记。 图标覆盖设置 右键菜单打开 &#xff1a;设置 》 图标覆盖。 隐藏图标覆盖 如果不想图标上出现小乌龟的状态标记。直接点这里可以…

Chrome浏览器调用ActiveX控件--allWebOffice控件

背景 allWebOffice控件能够实现在浏览器窗口中在线操作文档的应用&#xff08;阅读、编辑、保存等&#xff09;&#xff0c;支持编辑文档时保留修改痕迹&#xff0c;支持书签位置内容动态填充&#xff0c;支持公文套红&#xff0c;支持文档保护控制等诸多办公功能&#xff0c;…

docker部署redis使用键空间通知订阅

1. redis的键空间通知(keyspace notification) 大概意思是比如时间删除 或者 过期的时候发布的一个通知。通过一个通知来发布或修改一个状态。通常理解为 pub/sub&#xff08;发布/订阅&#xff09;。 键空间通知有两种类型key-space , key-event, 可以理解为key-space对应事件…

(堆) 优先队列(堆)的简单实现

文章目录 &#x1f3d4;️堆是什么&#xff1f;&#x1f3d4;️C 标准库&#x1f3d4;️手动实现⛰️原理简介⛰️C⛰️C语言 ⭐END&#x1f31f;交流方式 &#x1f3d4;️堆是什么&#xff1f; 堆简介 - OI Wiki 堆是一棵树&#xff0c;其每个节点都有一个键值&#xff0c;且…

【EXCEL】 获取多列中 不为空的那一个数据

从多个表格筛选出来的上班时间是下表这样的 我要把他们放在同一列&#xff0c;这样方便后续处理&#xff0c;合并列输入下面这个公式即可 日期不加 TEXT&#xff08;&#xff09; 函数 转日期格式&#xff1b;将得到是一串数字 TEXT(TEXTJOIN(", ",TRUE,B2:F2),&qu…

springboot+Loki+Loki4j+Grafana搭建轻量级日志系统

文章目录 前言一、日志组件介绍 1.1 Loki组件1.2 Loki4j组件1.3 Grafana 二、组件下载安装运行 Loki下载安装运行Grafana下载安装运行 三、创建springboot项目总结 前言 日志在任何一个web应用中都是不可忽视的存在&#xff0c;它已经成为大部分系统的标准组成部分。搭建日志…

自然语言处理:从入门到精通全指引

一、引言 自然语言处理&#xff08;NLP&#xff09;作为人工智能领域的关键分支&#xff0c;旨在让计算机理解、生成和处理人类语言&#xff0c;近年来取得了令人瞩目的成就&#xff0c;在智能客服、机器翻译、文本分析、语音助手等众多领域发挥着重要作用。从入门到精通自然语…

opencvocr识别手机摄像头拍摄的指定区域文字,文字符合规则就语音报警

安装python&#xff0c;pycharm&#xff0c;自行安装。 Python下安装OpenCv 2.1 打开cmd,先安装opencv-python pip install opencv-python --user -i https://pypi.tuna.tsinghua.edu.cn/simple2.2 再安装opencv-contrib-python pip install opencv-contrib-python --user …

智能时代网络空间认知安全新观察

文章目录 前言一、历史上的四次认知革命二、人工智能革命掀起认知安全新浪潮三、人工智能技术塑造认知安全新范式四、人工智能治理应对认知安全新思考 前言 12月5日&#xff0c;在2024第三届北外滩网络安全论坛上以“智能时代网络空间认知安全新观察”为主题作主旨演讲&#x…

【Python标准库】subprocess

1. subprocess是什么? 输入终端命令进行操作 2.用法 run() 作用:创建新的进程执行外部命令,并等待它们完成​​​​​​​语法:run([command, arg1, arg2], capture_output=True, text=True,check,timeout) [command, arg1, arg2], 是单独运行的命令和参数,作为列表来处…

springboot417社区疫情管理系统-(论文+源码)_kaic

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统社区疫情管理系统信息管理难度大&#xff0c;容错率低&a…

信创国产化时代:打造安全高效的信创网站解决方案

在全球科技竞争日益激烈的背景下&#xff0c;信创国产化已经成为中国信息技术领域的重要战略选择。信创国产化&#xff0c;即信息技术应用创新与国产化&#xff0c;旨在通过自主研发和创新&#xff0c;推动核心技术的国产化&#xff0c;减少对外部技术的依赖&#xff0c;确保国…

day10性能测试(2)——Jmeter

【没有所谓的运气&#x1f36c;&#xff0c;只有绝对的努力✊】 目录 1、LoadRunner vs Jmeter 1.1 LoadRunner 1.2 Jmeter 1.3 对比小结 2、Jmeter 环境安装 2.1 安装jdk 2.2 安装Jmeter 2.3 小结 3、Jmeter 文件目录结构 4、Jmeter默认配置修改 5、Jmeter元件、组…

可造成敏感信息泄露!Spring Boot之Actuator信息泄露漏洞三种利用方式总结

1.介绍 Spring Boot是一个基于Spring的套件&#xff0c;它提供了一个即开即用的应用程序架构&#xff0c;可以简化Spring应用的创建及部署流程&#xff0c;帮助开发者更轻松快捷地构建出企业及应用。 Spring Boot项目中Actuator模块提供了众多HTTP接口端点&#xff08;Endpoi…

KV Shifting Attention Enhances Language Modeling

基本信息 &#x1f4dd; 原文链接: https://arxiv.org/abs/2411.19574&#x1f465; 作者: Mingyu Xu, Wei Cheng, Bingning Wang, Weipeng Chen&#x1f3f7;️ 关键词: KV shifting attention, induction heads, language modeling&#x1f4da; 分类: 机器学习, 自然语言处…

MongoDB-固定集合(Capped Collection)

在 MongoDB 中&#xff0c;固定集合&#xff08;Capped Collection&#xff09;是一种具有特殊属性的集合。固定集合具有一个固定的最大大小&#xff0c;并且一旦达到该大小时&#xff0c;最早插入的文档将会被自动删除&#xff0c;以便为新的文档腾出空间。固定集合的这种特性…

力扣 螺旋矩阵-54

螺旋矩阵-54 class Solution { public:vector<int> spiralOrder(vector<vector<int>>& matrix) {int m matrix.size(),n matrix[0].size();//数组的行列大小int top0,downm-1,left 0,right n-1;//分别为矩阵上下左右的边界int index 0;//存放结果数…