静态依赖注入 VS 动态依赖查询

一、一句话定义

方式 核心特征 通俗理解
静态依赖注入 Spring 自动把依赖塞进来 食堂阿姨给你打饭,你坐着等就行
动态依赖查找 你主动去容器里找 Bean 自助餐,你自己起身去拿

二、静态依赖注入(Static Dependency Injection)

代码长这样

java 复制代码
@Service
public class OrderService {
    
    // 方式1:字段注入(最常见)
    @Autowired
    private UserService userService;
    
    // 方式2:构造方法注入(Spring 推荐)
    public OrderService(UserService userService) {
        this.userService = userService;
    }
    
    // 方式3:Setter 注入
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}

特点

  • 启动时就定死了:Spring 扫描到 @Autowired,启动阶段就会把 UserService 的对象注入进来。

  • 你完全不用管:依赖怎么来的、什么时候创建的,你都不关心。

  • 依赖关系明确 :看一眼代码,就知道 OrderService 依赖 UserService。


三、动态依赖查找(Dynamic Dependency Lookup)

代码长这样

java 复制代码
@Service
public class OrderService implements ApplicationContextAware {
    
    private ApplicationContext context;
    
    @Override
    public void setApplicationContext(ApplicationContext ctx) {
        this.context = ctx;
    }
    
    public void createOrder(String type) {
        // 运行时,根据参数动态去容器里"查找"对应的 Bean
        PaymentStrategy strategy = context.getBean(type + "Strategy", PaymentStrategy.class);
        strategy.pay();
    }
}

或者更直接的:

java 复制代码
@Autowired
private ApplicationContext context;

public void doSomething() {
    // 需要的时候才去"拿"
    UserService userService = context.getBean(UserService.class);
}

特点

  • 运行时才能确定:代码写的时候不知道要拿哪个 Bean,运行时才决定。

  • 你主动伸手 :不是 Spring 塞给你,是你主动 getBean()。

  • 耦合了 Spring 容器 :你的代码里出现了 ApplicationContext,离开 Spring 框架这段代码就没法跑了。


四、核心区别对比

对比项 静态依赖注入 动态依赖查找
代码写法 @Autowired、构造方法 context.getBean(...)
时机 容器启动时注入 运行时按需查找
依赖是否明确 明确,写死在代码里 不明确,运行时动态决定
是否耦合 Spring 不耦合(注解可被其他框架识别) 强耦合 ApplicationContext
灵活性 低,依赖固定 高,可以动态切换
可测试性 好,直接 Mock 依赖 差,需要模拟 Spring 容器
典型场景 绝大多数日常开发 策略模式、插件化、工厂模式

五、为什么需要"动态依赖查找"?

你可能会问:静态注入这么好用,为什么还要主动 getBean()?

场景 1:策略模式(运行时决定用哪个实现)

java 复制代码
@Service
public class PayFactory implements ApplicationContextAware {
    
    private ApplicationContext context;
    
    public void pay(String channel) {  // channel = "alipay" 或 "wechat"
        // 启动时不知道用户会选哪种支付方式,只能运行时去查
        PaymentService service = context.getBean(channel + "PayService", PaymentService.class);
        service.pay();
    }
}

这里你不可能 写 @Autowired,因为启动时不知道用户会传 "alipay" 还是 "wechat"。

场景 2:解决循环依赖(应急方案)

java 复制代码
@Service
public class A {
    
    @Autowired
    private ApplicationContext context;
    
    // 构造方法注入会导致循环依赖时,可以延迟查找
    public void doSomething() {
        B b = context.getBean(B.class);  // 用的时候才拿,打破循环
        b.doSomething();
    }
}

场景 3:获取非单例 Bean(Prototype)

复制代码
// 每次都需要一个新的对象
TaskExecutor executor = context.getBean(TaskExecutor.class);

六、面试常问:ApplicationContextAware 属于哪种?

标准答案:

ApplicationContextAware 本身是 Aware 接口回调 (属于生命周期钩子),但它赋予了你"动态依赖查找"的能力。

也就是说:

  • 实现 ApplicationContextAware → 是为了拿到 Spring 容器

  • 拿到容器后调用 getBean() → 这就是动态依赖查找

面试官追问 :既然能动态查找,是不是所有依赖都用 getBean() 更好?

答 :不是。能用静态注入的,绝对不要用动态查找。因为:

  1. 代码耦合 Spring 容器,不好单元测试

  2. 隐藏了类的依赖关系,代码可读性差

  3. 违背了"控制反转"的初衷(IoC 就是让你被动接收,而不是主动索取)


七、一张图记住

复制代码
【Spring 容器】
     │
     ├─ 静态注入 ──→ Spring 主动推给你 ──→ @Autowired
     │                  "你坐着,饭送嘴边"
     │
     └─ 动态查找 ──→ 你主动找容器要 ──→ getBean()
                        "你自己去窗口端菜"

八、总结

你的情况 用哪个
依赖关系明确,启动时就知道要谁 静态注入 @Autowired
运行时才能确定要哪个 Bean 动态查找 getBean()
写框架、做插件化、策略工厂 可以用动态查找
写普通业务代码 只用静态注入,别碰 getBean()

记住一个原则:动态查找是"杀手锏",不是"日常武器"。 日常开发里看到有人到处 getBean(),通常说明代码设计有问题。

相关推荐
开开心心就好1 小时前
音频降噪剪辑工具,音量裁剪一键处理
运维·服务器·前端·spring·ocr·音视频·1024程序员节
Flynn Liang2 小时前
大模型怎么自己调函数?Spring AI 2.0 Tool Calling 自动循环原理 + 完整时序图
java·人工智能·spring·agent·函数调用·springai·toolcalling
乌暮3 小时前
请求进门先安检:Spring 拦截器与统一功能实战指南
java·开发语言·后端·学习·spring
CoderYanger7 小时前
Java EE 进阶:5. Spring IoC&DI(第三弹):Spring DI
java·开发语言·spring boot·spring·面试·java-ee·mvc
郭小李7 小时前
spring中@Transactional注解对事务的支持
数据库·spring·oracle
步行cgn20 小时前
FormHttpMessageConverter 详解:Spring MVC 中表单数据的读写转换器
java·spring·mvc
霸道流氓气质1 天前
智能客服组件:分层意图识别、RAG Advisor 与人工转接状态机的 Vue3 + Spring AI 生产级实战
java·人工智能·spring
captain3761 天前
MyBatis操作数据库(入门2)
java·数据库·spring boot·后端·spring·mybatis
步行cgn1 天前
Spring MVC + Thymeleaf 模板解析失败:从 500 错误到页面正常显示
java·spring·mvc
学到老活到老1 天前
第 24 集:配置文件写了 80,Spring Boot 为什么读到 90?
java·后端·spring