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

相关推荐
杨杨杨大侠6 小时前
RAG 到底是怎么工作的?从用户提问到模型回答的完整链路
spring·openai·ai编程
Joy T6 小时前
Spring AI 2.0 Agent 进阶:Memory、State 与 Context Engineering 常见技术全景
java·人工智能·后端·spring·agent入门·agent state
wno7048 小时前
Spring Security短信验证码登录
java·python·spring
步行cgn9 小时前
Spring util 命名空间详解
java·后端·spring
MayBaymax11 小时前
Spring AI Alibaba Graph 快速上手:黑板、节点、边
java·spring·ai·ai编程
acd1200913 小时前
Java 线程池线上高频踩坑记录
spring
苏渡苇14 小时前
Spring Insight 里如何把 Span 收成一条链路
spring boot·spring·spring cloud·系统监控·apm
小尹哥-程序员17 小时前
第4集:让AI学会看文档:Spring AI RAG入门实战
java·人工智能·spring
叶总没有会17 小时前
微服务--分布式基础
java·spring·spring cloud·微服务
考虑考虑1 天前
Springboot环境变量占位符语法
spring boot·后端·spring