依赖注入将实现选择权交给外部,使调用方仅依赖接口、不耦合具体实现;构造函数注入最稳妥,spring通过@autowired自动装配,多实现时用@qualifier指定,运行时可通过setter或getbean动态切换。

接口与实现类之间的依赖注入,本质是把“用哪个实现”这件事从代码里拿出去,交给外部决定。这样改实现不用动调用方,加新功能不碰老逻辑,测试也方便替换成模拟对象。
用接口定义契约,实现类只管干活
先定义一个清晰的接口,比如用户通知服务:
- 接口只声明能力:public interface NotifyService { void send(String content); }
- 多个实现互不影响:EmailNotifyService、SmsNotifyService、WechatNotifyService 各自实现 send 方法,彼此不耦合
- 调用方只依赖接口:UserService 里声明 private final NotifyService notifyService; —— 它不知道也不该关心具体是发邮件还是发短信
构造函数注入:最稳的起步方式
在创建对象时就把依赖塞进去,保证一出生就有“搭档”,避免空指针和状态不一致:
- UserService 构造器写成 public UserService(NotifyService notifyService) { this.notifyService = notifyService; }
- 外部创建时决定用哪个:new UserService(new SmsNotifyService()) 或 new UserService(new EmailNotifyService())
- Spring 中只需加 @Autowired 注解,容器自动匹配符合条件的实现类(注意:若多个实现存在,需用 @Qualifier 指定)
运行时切换实现:靠 Setter 或重新注入
有些场景需要中途换依赖,比如灰度发布时切部分流量到新通知渠道:
- 提供 public void setNotifyService(NotifyService service) 方法,允许后期替换
- Spring 中配合 @Lookup 或 @Bean 方法动态返回不同实例,也可用 ApplicationContext.getBean(NotifyService.class, "sms") 按名称获取
- 注意线程安全:Setter 方式会让对象状态可变,多线程环境下需额外同步或改用不可变设计
框架不是必须,但能省掉重复劳动
手动注入适合小项目或教学理解;真实业务中,Spring 的自动扫描+注解机制大幅减少样板代码:
- @Service 标记业务类,@Component 标记工具类,@Repository 标记数据访问类
- 所有实现类加 @Component(或其派生注解),接口本身不加注解
- 启动类上加 @SpringBootApplication,容器自动完成装配,无需 new 或 set
- 想临时禁用某实现?直接加 @ConditionalOnProperty 或注释掉 @Component 即可
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











