Skip to content

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」)。

我的三个典型架构用法:

  1. 环境默认实现切换:@Profile("dev") 的内存实现标 Primary,生产环境由 Redis 实现接管,业务代码零感知;
  2. 多实现演进期共存:新旧实现灰度共存,Primary 控制默认走向,配合开关在运行期切流;
  3. 覆盖自动配置:自定义 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。