当前位置: 首页 > news >正文

面试:Spring

1.依赖注入(IOC)高频问题解析

1.1 Spring的IOC是什么意思?

  • 核心定义
    IOC(Inversion of Control,控制反转)是设计思想,将对象的 创建、依赖管理权限 从开发者转移到Spring容器。
  • 实现方式(DI)
    通过 依赖注入(Dependency Injection)完成,包括 构造器注入、Setter注入、字段注入(如@Autowired)。
  • 对比传统模式
    传统开发中,开发者需手动new对象并维护依赖(如UserService service = new UserService(new UserDao()));IOC下,容器统一管理Bean的生命周期和依赖,解耦对象关系。
  • 总结:IOC让开发者聚焦业务,容器负责“对象编排”,是Spring解耦的核心。

1.2 Spring中的Bean是线程安全的吗?

  • 分场景判断
    • 单例(默认)
      • 若Bean是 无状态(无成员变量或变量只读),天然线程安全(如工具类StringUtils);
      • 若含 可变成员变量(如private String name),多线程共享会引发竞争(如并发修改name)。
      • 解决方法
        • 避免定义可变状态;
        • ThreadLocal隔离线程变量;
        • 改为原型(prototype)(每次请求新建Bean,性能开销大);
        • 加锁(synchronized,影响效率)。
    • 原型(prototype):每次获取新实例,无共享问题,线程安全。

1.3 什么是Spring的三级缓存?

  • 缓存结构(解决单例Bean循环依赖的核心):
    缓存层级作用存储对象状态
    一级缓存(singletonObjects存储 完全初始化完成 的单例Bean成品Bean(可直接使用)
    二级缓存(earlySingletonObjects存储 实例化后、未初始化 的半成品Bean半成品(已实例化,未注入依赖/初始化)
    三级缓存(singletonFactories存储 Bean的工厂函数(生成早期引用)工厂Lambda(创建早期Bean)
  • 工作流程(以A→B→A循环依赖为例):
    1. 创建A:实例化后,将A的工厂放入三级缓存,尝试注入B
    2. 创建B:实例化后,从三级缓存取A的早期引用(放入二级缓存,删除三级缓存),完成B的初始化并放入一级缓存。
    3. 回到A:从二级缓存取B的成品,完成A的初始化,最终放入一级缓存。

1.4 Spring的循环依赖问题是什么?如何解决?

  • 问题定义
    多个Bean互相依赖(如A依赖BB依赖A),导致创建时相互等待,无法初始化。
  • 解决范围
    • 支持Setter注入/字段注入(依赖注入发生在实例化后,可通过三级缓存提前暴露引用)。
    • 不支持构造器注入(依赖注入发生在实例化阶段,此时Bean未实例化,无法提前暴露),会抛BeanCurrentlyInCreationException
  • 解决原理
    利用三级缓存,在Bean 实例化后(未初始化) 提前暴露引用,打破循环等待。

2. AOP原理高频问题解析

2.1 SpringAOP的实现原理是什么?

  • 核心:动态代理,运行时生成目标对象的代理类,拦截方法调用并增强。
  • 两种代理方式
    • JDK动态代理
      • 基于接口(目标类必须实现接口),通过java.lang.reflect.Proxy生成代理,重写invoke方法。
      • 优点:JDK原生支持,性能高;缺点:必须依赖接口。
    • CGLIB代理
      • 基于类继承(目标类无需接口),通过字节码增强生成子类,重写目标方法。
      • 优点:支持类代理;缺点:无法代理final类/方法(子类无法继承)。
  • Spring策略
    • 目标类有接口:优先JDK代理;
    • 目标类无接口或配置强制CGLIB(如Spring Boot默认):用CGLIB。
  • 织入时机运行时(区别于AspectJ的编译时织入),代理对象替代目标对象执行,插入通知(Before、After等)。

2.2 Spring的AOP在什么场景下会失效?

常见失效场景

  1. 目标对象未被Spring管理
    手动new对象(如UserService service = new UserService()),而非从容器获取,代理不生效。
  2. 方法是final/static
    • final方法:CGLIB无法继承重写,JDK代理也无法拦截(接口方法不能是final);
    • static方法:属于类,代理针对实例方法,无法拦截。
  3. 同一类内的方法调用(this调用)
    class A { public void method1() { this.method2(); } }method2的切面不生效——因this是目标对象,而非代理对象,跳过拦截。
  4. 代理方式不匹配
    目标类有接口,但强制用CGLIB代理,且方法是final,则代理失败(CGLIB无法重写final方法)。

3. Bean生命周期与创建高频问题解析

3.1说一下Spring Bean的生命周期是怎么样的?

单例Bean完整流程

  1. 实例化(Instantiation):通过构造器/工厂方法创建Bean实例(内存分配,未注入依赖)。
  2. 属性注入(Populate):注入依赖(@Autowired、<property>等)。
  3. 初始化前(BeanPostProcessor前置处理):执行BeanPostProcessor.postProcessBeforeInitialization,可修改Bean属性(如统一加日志)。
  4. 初始化(Initialization)
    • 实现InitializingBean:调用afterPropertiesSet
    • 配置init-method:执行自定义初始化逻辑。
  5. 初始化后(BeanPostProcessor后置处理):执行BeanPostProcessor.postProcessAfterInitialization此处是AOP生成代理的关键时机(替换目标对象为代理)。
  6. 使用(Usage):Bean进入容器,供其他Bean调用。
  7. 销毁(Destruction):容器关闭时,实现DisposableBean则调用destroy,或执行destroy-method

3.2聊聊Spring的BeanFactory和FactoryBean?

  • BeanFactory
    • 角色:Spring容器的顶层接口,定义IOC容器核心能力(如getBean()containsBean()),管理所有Bean的生命周期。
    • 典型实现DefaultListableBeanFactory(Spring内部核心容器)。
  • FactoryBean
    • 角色特殊的Bean(由BeanFactory管理),作用是创建其他Bean(工厂模式)。
    • 场景:复杂对象创建(如MyBatis的SqlSessionFactoryBean,构建SqlSessionFactory)。
    • 核心方法
      • getObject():返回实际要创建的Bean;
      • getObjectType():返回Bean类型;
      • isSingleton():是否单例。
  • 类比:BeanFactory是“容器管理者”,FactoryBean是“Bean的工厂工人”——前者管所有Bean,后者专门生产某类Bean。

面试答题技巧

  1. 分层拆解:如Bean生命周期分阶段,结合接口(InitializingBean)和配置(init-method),体现细节。
  2. 对比强调:如BeanFactory vs FactoryBean,从“角色”“作用”维度区分,避免混淆。
  3. 原理+场景:解释三级缓存时,结合“A→B→A循环依赖”讲流程;讲AOP失效时,举“this调用”的代码例子。
  4. 避坑提示:如构造器注入的循环依赖无法解决,单例Bean的线程安全问题,体现思考深度。
http://www.dtcms.com/a/352297.html

相关文章:

  • 30.LSTM-长短时记忆单元
  • 抢红包案例加强版
  • 并行多核体系结构基础——共享存储并行编程(笔记)
  • 网络编程close学习
  • Java大厂面试实录:从Spring Boot到Kubernetes的全链路技术突围
  • python命名规则(PEP 8 速查表),以及自定义属性
  • 深度感知卷积和深度感知平均池化
  • python自动测试 crictl 可以从哪些国内镜像源成功拉取镜像
  • pulsar、rocketmq常用命令
  • C#由Dictionary不正确释放造成的内存泄漏问题与GC代系
  • Text to Speech技术详解与实战:GPT-4o Mini TTS API应用指南
  • 从“脚本语言”到“企业级引擎”——PHP 在 2025 年技术栈中的再定位
  • Linux服务器安全配置与NTP时间同步
  • 记录一下,qt问题:qt ui文件的改动无法更新到cpp
  • 疯狂星期四文案网第51天运营日记
  • Typescript入门-interface讲解
  • 类型签名,位置参数,关键字参数
  • open webui源码分析8—管道
  • 域名常见问题集(十一)——为什么要进行域名管理?
  • 【实时Linux实战系列】基于实时Linux的音频实时监控系统
  • 从16个粉丝到680万年收入:AI创业的117天奇迹
  • 声明式微服务通信新范式:OpenFeign如何简化RestTemplate调用
  • Windows下实现类似`watch nvidia-smi`的实时监控效果
  • 进入docker中mysql容器的方法
  • Java:TreeSet的使用
  • (Arxiv-2024)VideoMaker:零样本定制化视频生成,依托于视频扩散模型的内在力量
  • QT qml(quick3D)模型的移动
  • 专业解读《Light》封面:可调谐混合超表面(THCMs)如何革新下一代LiDAR系统
  • 3D游戏角色建模资源搜索指南(资料来源于网络)
  • 湖仓一体:小米集团基于 Apache Doris + Apache Paimon 实现 6 倍性能飞跃