Spring 5中如何阻止实现接口的Service Bean被注册为该接口的自动装配候选
你遇到的问题很典型:Kotlin的接口委托让你的IncidentBindService自动实现了IncidentBindRepository接口,Spring的类型匹配机制会把这个Service也当成IncidentBindRepository类型的候选Bean,和真正的Repository实现(比如IncidentBindDynamoRepository)形成冲突。而且你不想通过注入具体实现类来牺牲代码的可测试性,这完全合理。
下面给你几个实用的解决方案,按推荐程度排序:
1. 给真正的Repository添加@Primary注解
这是最简单直接的方案,不需要修改注入点或Service的结构。只需要在你的Repository实现类上标记@Primary,告诉Spring当有多个同类型Bean时,优先选择这个:
@Repository @Primary class IncidentBindDynamoRepository : IncidentBindRepository { // 你的Repository实现代码 }
这样Spring在自动装配IncidentBindRepository类型时,会直接选中带有@Primary的真正Repository,完全忽略Service这个候选,同时你仍然可以保持注入接口类型,不影响可测试性。
2. 用@Qualifier区分Bean
如果你的项目中有多个IncidentBindRepository实现,@Primary可能不够灵活,这时候可以用@Qualifier来明确指定要注入的Bean:
首先给真正的Repository添加Qualifier:
@Repository @Qualifier("incidentBindRepository") class IncidentBindDynamoRepository : IncidentBindRepository { // ... }
然后在需要注入的地方指定这个Qualifier:
@Autowired @Qualifier("incidentBindRepository") private val incidentBindRepository: IncidentBindRepository
这个方案也能保持接口注入的可测试性,只是需要修改所有注入IncidentBindRepository的地方。
3. 让Spring Data扫描时排除Service类
如果你的问题是Spring Data把Service误识别为Repository Bean(毕竟Service实现了Repository接口),可以在启用Repository扫描的注解里添加排除规则,比如Spring Data DynamoDB的@EnableDynamoDBRepositories:
@SpringBootApplication @EnableDynamoDBRepositories( basePackages = ["com.your.package.repository"], excludeFilters = [ComponentScan.Filter( type = FilterType.ASSIGNABLE_TYPE, classes = [IncidentBindService::class] )] ) class YourApplication
这样Spring Data在扫描Repository时会跳过你的Service类,不会把它注册为Repository类型的Bean,而Service仍然是正常的@Service Bean,同时保留了委托带来的方法便利性。
4. 避免Service直接实现Repository接口(不推荐)
如果上面的方案都不适合你,你可以放弃Kotlin的接口委托,改为在Service内部手动委托Repository方法,这样Service就不会实现Repository接口,自然不会产生冲突:
@Service class IncidentBindService( @Autowired private val incidentBindRepository: IncidentBindRepository ) { // 手动委托需要暴露的Repository方法 fun findById(id: String): Optional<IncidentBind> = incidentBindRepository.findById(id) fun save(entity: IncidentBind): IncidentBind = incidentBindRepository.save(entity) // ... 其他需要的Repository方法 // 你的额外业务方法 }
这个方案的缺点是需要手动编写所有委托方法,失去了Kotlin委托的简洁性,所以只作为最后的备选。
内容的提问来源于stack exchange,提问作者Ondra Žižka

