Spring 中的@Primary 注解的作用是什么?
- 标签:后端、Spring
🏛 业务架构师
@Primary 的作用是:当一个接口存在多个候选 Bean 时,标记其中一个为"默认首选",按类型自动装配不再报歧义错误,直接注入首选者。
业务上它解决的真问题是"主备实现的默认路由":
- 短信服务接了腾讯云(主)和阿里云(备),
@Primary标在腾讯云实现上,所有默认注入@Autowired SmsService的地方自动走主通道,需要特殊指定的地方才用@Qualifier精确点名; - 本地开发用 H2 内存库、测试环境标它为首选,生产环境换成 MySQL 实现——配合
@Profile组合成环境差异装配。
和 @Qualifier 的分工我一句话概括:@Primary 定默认,@Qualifier 开例外。默认路径免配置,例外路径显式声明,两者的使用比例反映设计的健康度——到处 @Qualifier 说明路由关系混乱。
一个实践提醒:@Primary 用在 starter 里要克制。平台组件给业务注入默认实现没问题,但如果多个 jar 都声明了自己的 Primary,冲突排查会很痛苦。公司内部建议由架构组统一约定哪些接口允许有 Primary 默认值,避免"谁后加载谁生效"的隐性行为。另外 Spring Boot 的自动配置大量使用它(如默认的 ObjectMapper、RestTemplateBuilder),理解这个机制也帮你读懂框架的扩展点。
🏗 系统架构师
作用:同类型出现多个候选 Bean 时,标记「默认首选」,让按类型注入可以唯一解析。
与 @Qualifier 的分工:Primary 是全局默认实现(「通常都用 A」),Qualifier 是个别点名(「这里必须用 B」)。
我的三个典型架构用法:
- 环境默认实现切换:
@Profile("dev")的内存实现标 Primary,生产环境由 Redis 实现接管,业务代码零感知; - 多实现演进期共存:新旧实现灰度共存,Primary 控制默认走向,配合开关在运行期切流;
- 覆盖自动配置:自定义 Bean 标
@Primary覆盖 starter 提供的默认组件(比如自定义 ObjectMapper),这是不改框架代码替换组件的标准姿势。
注意点:两个 Bean 都标 Primary 启动即失败(好事,fail-fast);跨模块标 Primary 要在架构上收口,否则「谁是默认」会变成模块间的隐藏耦合。
💻 全栈开发专家
作用:同类型多候选 Bean 时标记"默认首选",让按类型注入不再抛 NoUniqueBeanDefinitionException:
java
@Configuration
public class CacheConfig {
@Bean
@Primary
public CacheManager localCacheManager() { return new CaffeineCacheManager(); }
@Bean
public CacheManager redisCacheManager(RedisConnectionFactory f) {
return RedisCacheManager.builder(f).build();
}
}上面就是典型生产场景:默认走本地 Caffeine,显式 @Qualifier("redisCacheManager") 的地方才走 Redis——默认配置 + 特例覆盖的组合拳。
行为细节:@Primary 只在"没有更精确限定"时生效,优先级 @Qualifier(精确指名)> @Primary(默认值)> 参数名匹配;集合注入(List<T>、Map<String,T>)不受影响,照样收全量。
Boot 自动配置大量用它,比如默认 DataSource 标 @Primary——这也是多数据源必须给一套标 @Primary 的原因,否则事务管理器、SqlSessionFactory 全报候选冲突,启动就过不去。一句话:单点注入选默认用 Primary,点名用 Qualifier。