关于 spring-boot-dependency-injection
面向 Spring Boot 开发者的依赖注入技能,侧重构造器注入和明确的 Bean 装配。还介绍通过独立单元测试与最小上下文测试及早发现装配问题。
主要功能
- 01必需依赖使用构造器注入
- 02明确处理可选协作者
- 03使用 @Primary 和 @Qualifier 选择 Bean
- 04在配置类中装配对象
- 05最小上下文与构造器测试
适用场景
- 01创建 Spring 服务与组件
- 02替换旧代码中的字段注入
- 03选择同类型的多个 Bean
- 04在完整集成前验证装配
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ giuseppe-trisciuoglio/ developer-kit --skill spring-boot-dependency-injection安装后在 Claude Code 中输入/spring-boot-dependency-injection确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/examples.mdreference.mdspring-official-dependency-injection.md详细介绍
Spring Boot 依赖注入
概述
为 Spring Boot 提供以构造函数优先的依赖注入模式:
- 通过构造函数注入必需的协作者
- 通过
ObjectProvider或空操作回退方案处理可选协作者 - 通过
@Primary和@Qualifier选择 Bean - 在完整集成前,通过最小化上下文测试进行验证
适用场景
在以下情况下使用此技能:
- 创建新的
@Service、@Component、@Repository或@Configuration类 - 替换遗留 Spring 代码中的字段注入
- 使用限定符或主 Bean 解决同一类型存在多个 Bean 的问题
- 处理可选功能、适配器或集成,避免依赖
null进行装配 - 检查循环依赖或脆弱的上下文启动故障
- 为直接通过构造函数进行单元测试做好代码准备
指南
1. 区分必需和可选协作者
对于每个类,明确以下内容:
- 正常运行所必需的协作者
- 用于启用集成、缓存、通知或特性开关行为的可选协作者
必需的协作者应放在构造函数中。可选协作者需要采用明确的策略,例如 ObjectProvider、条件 Bean 或空操作实现。
2. 默认使用构造函数注入
对于应用服务和适配器:
- 通过构造函数注入必需依赖
- 将注入的字段设为
final - 在单元测试中直接实例化类,无需启动 Spring
通常只需一个构造函数;这种情况下不需要 @Autowired。
3. 有意处理可选行为
合适的选项包括:
- 在延迟访问有用时使用
ObjectProvider<T> - 在装配应随配置变化时使用
@ConditionalOnProperty或@ConditionalOnMissingBean - 在调用方无需关心功能是否启用时使用空操作实现
避免使用可为 null 的协作者,以免运行时行为变得模糊。
4. 仅在必要时使用 Bean 选择注解
当多个 Bean 具有相同类型时:
- 使用
@Primary指定默认实现 - 使用
@Qualifier指定命名变体 - 保持限定符名称稳定且易于搜索
如果选择规则变得复杂,请将其移到专用配置类中,而不是分散到各个服务里。
5. 将装配逻辑放在配置中,而非业务代码中
在以下情况下使用 @Configuration 和 @Bean 方法:
- 对象来自第三方库
- 需要条件创建逻辑
- 需要特定于环境的装配或显式组合
业务服务不应了解基础设施协作者的实例化方式。
6. 显式验证装配
编写新的服务或配置后:
- 使用最小化上下文测试验证 Bean 是否加载:
@SpringBootTest @ContextConfiguration(classes = UserService.class) class UserServiceWiringTest { @Autowired UserService userService; @Test void serviceIsInstantiated() { assertNotNull(userService); } } - 运行基于构造函数的单元测试,验证服务行为(无需 Spring)。
- 仅在必须验证 MVC、JPA 或消息传递集成时,添加切片测试。
- 将
@SpringBootTest留作容器级装配验证之用。
第 1 步出现故障,说明在添加业务逻辑前就存在装配问题。
示例
示例 1:构造函数优先的应用服务
@Service
public class UserService {
private final UserRepository userRepository;
private final EmailSender emailSender;
public UserService(UserRepository userRepository, EmailSender emailSender) {
this.userRepository = userRepository;
this.emailSender = emailSender;
}
public User register(UserRegistrationRequest request) {
User user = userRepository.save(User.from(request));
emailSender.sendWelcome(user);
return user;
}
}
此类便于在单元测试中直接使用模拟对象进行实例化。
示例 2:带空操作回退方案的可选依赖
@Service
public class ReportService {
private final ReportRepository reportRepository;
private final NotificationGateway notificationGateway;
public ReportService(
ReportRepository reportRepository,
ObjectProvider<NotificationGateway> notificationGatewayProvider
) {
this.reportRepository = reportRepository;
this.notificationGateway = notificationGatewayProvider.getIfAvailable(NotificationGateway::noOp);
}
}
这样可以明确表达可选行为,同时避免在类的其余部分传播 null 处理逻辑。
示例 3:通过明确规则选择多个 Bean
@Configuration
public class PaymentConfiguration {
@Bean
@Primary
PaymentGateway stripeGateway() {
return new StripePaymentGateway();
}
@Bean
@Qualifier("fallbackGateway")
PaymentGateway mockGateway() {
return new MockPaymentGateway();
}
}
使用 @Primary 作为默认路径,仅在需要特定变体时使用 @Qualifier。
最佳实践
- 对必需依赖,优先使用构造器注入。
- 保持服务构造器简洁;如果一个类需要太多协作者,说明设计可能需要引入另一个抽象。
- 使用无操作或条件 Bean,而不是可空的可选依赖。
- 将特定于框架的创建逻辑放在配置类中。
- 先在没有 Spring 的情况下测试服务,仅在能带来价值时再添加容器测试。
- 重构时移除字段注入,而不是继续扩展它。
限制和警告
- 字段注入会隐藏依赖,使测试更难编写。
- 循环依赖通常是设计问题,不是用
@Lazy解决的布线技巧。 - 过度使用限定符会让代码库难以理解;优先采用更好的抽象或更清晰的配置。
- 可选协作者在缺失时仍需有确定的行为。
- 如果过早使用完整上下文测试,可能会掩盖布线失败的真正原因。
参考资料
references/reference.mdreferences/examples.mdreferences/spring-official-dependency-injection.md
相关技能
spring-boot-crud-patternsspring-boot-rest-api-standardsunit-test-service-layer
评论
支持 Markdown