静态依赖注入 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(),通常说明代码设计有问题。

相关推荐
用户3126874877206 小时前
Spring Boot 异常处理到底怎么玩的?从 DispatcherServlet 到全局兜底的全链路拆解
spring
不才不才不不才6 小时前
Spring 源码系列(17): HandlerMapping 与 HandlerAdapter 两大体系
java·后端·spring
(轻舟已过万重山)7 小时前
第40章 Spring AI 实战:企业级 AI 应用架构
人工智能·spring·架构
堕落年代1 天前
Ollama CPU 推理大提示词优化实测报告(细致化数据版)
java·后端·spring
cfm_29141 天前
基于OAuth2.0实现微服务SSO单点登录
后端·spring·微服务·架构
攻城有术1 天前
专项攻克-springcloud及其组件
后端·spring·spring cloud
叶总没有会1 天前
5.1知识库概念进阶
java·人工智能·spring·阿里云·ai·原型模式
砍材农夫1 天前
物联网实战|Spring Boot MQTT平台 |emqx broker实战
spring boot·spring·spring cloud·mybatis
2401_894915531 天前
GEO 定位优化源码搭建常见报错排查:数据库、伪静态、接口调试
java·数据库·网络协议·tcp/ip·spring·unity