1. 什么是 IoC 和 DI?为什么要反转控制?

IoC(控制反转):对象创建和依赖管理的控制权从"对象自己"反转给"容器"(Spring IoC 容器)。你不再 new 依赖,而是声明需求,容器负责注入。

DI(依赖注入):IoC 的具体实现方式——容器在创建 Bean 时,把依赖的 Bean 自动注入进来(构造器/Setter/字段)。

为什么要反转:

反转前 vs 反转后
// ❌ 传统:自己 new,耦合具体实现,难以替换和测试
class OrderService {
    private final OrderMapper mapper = new OrderMapperImpl();
}

// ✅ IoC:声明依赖,由容器注入(构造器注入,推荐)
@Service
class OrderService {
    private final OrderMapper mapper;

    @Autowired
    public OrderService(OrderMapper mapper) {   // 容器发现依赖并注入
        this.mapper = mapper;
    }
}

🎯 面试要点

  • IoC 是思想(好莱坞原则:Don't call us, we'll call you),DI 是落地手段
  • 构造器注入优先(Bean 可被 final 修饰、依赖完整、防循环依赖告警)——Spring 官方推荐
  • @Autowired 按类型注入;有多个候选时报错 → 用 @Qualifier("name") 或 @Resource(name=) 指定

2. BeanFactory 和 ApplicationContext 的区别?

Spring Boot Web(Servlet) 环境默认是 AnnotationConfigServletWebServerApplicationContext,Reactive 是 AnnotationConfigReactiveWebServerApplicationContext,只有非 Web 应用才是 AnnotationConfigApplicationContext。

🎯 面试要点

  • 延迟加载 vs 预加载:预加载能启动时暴露配置错误,延迟加载省启动时间
  • 懒加载注解 @Lazy:仅对个别重 Bean 使用,别全局用
  • 面试延伸:DefaultListableBeanFactory 是所有容器的"地基"(BeanDefinition 注册中心)

3. 依赖注入的几种方式?@Autowired 和 @Resource 的区别?

三种注入方式:

@Autowired vs @Resource:

对比 @Autowired(Spring) @Resource(JSR-250)
匹配方式按类型,多候选再按名称先按名称,找不到再按类型
指定候选@Qualifiername 属性
作用范围字段/构造器/方法字段/Setter
必填控制required=false 可设为可选必填

🎯 面试要点

  • 多实现注入:List/Map 批量注入所有候选(如策略模式注册表);@Primary 指定默认
  • 字段注入的危害:循环依赖掩盖(构造器注入循环会直接报错,字段注入悄悄成立延迟到使用时才炸)
  • JavaConfig 推荐 @Bean 方法 + 构造器注入

4. Bean 有哪几种作用域?

经典坑:单例注入原型。singleton 依赖 prototype 时,注入发生在创建时——原型 Bean 只创建一次,之后每次用的是同一个。解决:

  1. 注入 ObjectFactory<T> 或 Provider<T>,每次使用调 get()
  2. @Lookup 方法(CGLIB 子类化,方法每次返回新实例)
  3. 把作用域改 request/proxyMode(@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS))

🎯 面试要点

  • Spring 的单例容器本身线程安全(Bean 注册表),但 Bean 的共享状态需要自己加锁/无状态化
  • Service/Mapper 等无状态 Bean 用 singleton 没问题;带可变状态的对象(计数器、Session)别放单例
  • @Scope("prototype") 时注意:依赖它的单例拿不到新实例(见上)

🎤 常见面试追问

  1. 为什么推荐构造器注入?——依赖不可变(final)、依赖完整(构造时就要给全,不会半初始化)、天然暴露循环依赖问题、方便单元测试。
  2. @Autowired 和 @Resource 的区别?——@Autowired 按类型(多候选按名称+@Qualifier);@Resource 先按名称再按类型(JSR-250)。
  3. 单例 Bean 线程安全吗?——容器保证"一个实例",不保证线程安全:无状态 Bean(Service/Mapper)安全;有可变状态的 Bean 要自己加锁或用 ThreadLocal。
  4. 单例注入原型 Bean 的坑?——注入发生在创建时,原型 Bean 只注入一次。解决:注入 ObjectFactory/Provider 每次 get,或 @Lookup 方法。
  5. BeanFactory 和 ApplicationContext 谁先创建?——ApplicationContext 是持有(组合)一个 BeanFactory,而不是由它"扩展"而来:AnnotationConfigApplicationContext 在构造时就 new DefaultListableBeanFactory(),refresh() 里的 obtainFreshBeanFactory() 只是获取(只有 XML 型上下文才在此创建)。

📖 名词解释(本页术语)

术语 大白话解释
IoC(控制反转)对象创建和依赖管理的控制权从"代码自己"交给容器。你不 new,你声明,容器给你。
DI(依赖注入)IoC 的实现方式:容器把依赖对象"送"进来(构造器/Setter/字段三种注入方式)。
Bean由 Spring 容器创建和管理的对象(@Component/@Service 标注的类)。
BeanFactory / ApplicationContext容器两级:BeanFactory 最底层(延迟加载);ApplicationContext 是其超集(预加载+事件+国际化),日常用的是后者。
@Component / @Service把类注册进容器的注解(@Service 语义是业务层,本质同 @Component)。
@Autowired让容器按类型注入依赖的注解。
Bean 作用域Bean 的存在范围:singleton(默认,全容器一个)/ prototype(每次新实例)/ request / session。
无状态 vs 有状态 Bean无状态:没有可变的成员字段(线程安全,可单例);有状态:带可变字段(要注意并发,或改 prototype)。
⚠️ 本页面由 AI 生成,内容仅供参考,请以官方文档和实际源码为准。