Spring 负责注入的注解详解

Spring 负责注入的注解详解

Spring 的注入注解分为两大体系:Spring 自有注解 (@Autowired、@Qualifier、@Primary、@Value)和 JSR 标准注解 (@Resource、@Inject、@Named)。它们共同解决同一个问题:把容器中的 Bean 或配置值注入到目标对象中。

一、注入注解总览

注解 来源 匹配方式 支持位置 特有属性
@Autowired Spring 先按类型,再按名称 字段、构造器、Setter、参数 required
@Qualifier Spring 按名称 字段、构造器、Setter、参数 value
@Primary Spring 标记首选 类、方法 ---
@Value Spring 按配置键 字段、构造器、Setter、参数 value
@Resource JSR-250 先按名称,再按类型 字段、Setter name、type
@Inject JSR-330 按类型 字段、构造器、方法 ---
@Named JSR-330 按名称 类、字段、参数 value

二、@Autowired

Spring 最核心的注入注解,默认按类型匹配。

2.1 三种注入位置

构造器注入(推荐):

java 复制代码
@Service
public class UserService {
    private final UserDao userDao;
    
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
}

类只有一个构造方法时,@Autowired 可以省略。

字段注入:

java 复制代码
@Service
public class UserService {
    @Autowired
    private UserDao userDao;
}

Setter 注入:

java 复制代码
@Service
public class UserService {
    private UserDao userDao;
    
    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

2.2 匹配规则

  1. 按类型查找容器中的 Bean。
  2. 找到多个同类型 Bean 时,按字段名或参数名匹配 Bean 名称。
  3. 仍无法确定时,尝试 @Primary 标记的 Bean。
  4. 最后用 @Qualifier 指定的名称匹配。

2.3 required 属性

java 复制代码
@Autowired(required = false)
private UserDao userDao;  // 找不到时注入 null,不报错

默认 required = true,找不到匹配 Bean 时抛 NoSuchBeanDefinitionException。

2.4 底层处理

AutowiredAnnotationBeanPostProcessor 处理 @Autowired、@Value、@Inject。它在 postProcessProperties() 中扫描字段和方法,通过 DefaultListableBeanFactory.resolveDependency() 解析依赖并注入。

三、@Qualifier

配合 @Autowired 使用,在同类型多 Bean 时按名称指定。

java 复制代码
@Service
public class OrderService {
    @Autowired
    @Qualifier("aliPayService")
    private PaymentService paymentService;
}

构造器参数上也可以使用:

java 复制代码
public OrderService(@Qualifier("aliPayService") PaymentService paymentService) {
    this.paymentService = paymentService;
}

@Qualifier 的优先级高于 @Primary。

四、@Primary

同类型多 Bean 时,标记其中一个为首选,解决 @Autowired 的歧义。

java 复制代码
@Service
@Primary
public class AliPayService implements PaymentService { }

@Service
public class WechatPayService implements PaymentService { }

@Service
public class OrderService {
    @Autowired
    private PaymentService paymentService;  // 注入 AliPayService
}

@Primary 可以标注在类上,也可以标注在 @Bean 方法上:

java 复制代码
@Bean
@Primary
public DataSource primaryDataSource() { ... }

五、@Value

注入简单类型和配置值,不涉及 Bean 之间的依赖。

java 复制代码
@Component
public class AppConfig {
    @Value("${app.name}")
    private String appName;
    
    @Value("${app.timeout:30}")
    private int timeout;
    
    @Value("${app.debug:false}")
    private boolean debug;
    
    @Value("#{${app.timeout} * 1000}")
    private int timeoutInMillis;
}

${key:default} 设置默认值,#{} 使用 SpEL 表达式。

@Value 的底层也是 AutowiredAnnotationBeanPostProcessor 处理,与 @Autowired 共用同一条注入链路。

六、@Resource

JSR-250 标准注解,由 JDK 提供,Spring 支持。默认按名称注入,找不到再按类型。

java 复制代码
@Service
public class UserService {
    @Resource(name = "userDaoImpl")
    private UserDao userDao;
}

不指定 name 时,默认使用字段名作为 Bean 名称:

java 复制代码
@Resource
private UserDao userDao;  // 按名称 "userDao" 查找

@Resource 与 @Autowired 的区别

对比维度 @Autowired @Resource
来源 Spring JSR-250(JDK)
匹配方式 先按类型,再按名称 先按名称,再按类型
required 属性 支持 不支持
构造器注入 支持 不支持
@Primary 支持 支持 不支持
适用场景 Spring 生态通用 按名称注入、非 Spring 环境

@Resource 由 CommonAnnotationBeanPostProcessor 处理。

七、@Inject 和 @Named

JSR-330 标准注解,需要引入 javax.inject 或 jakarta.inject 依赖。

xml 复制代码
<dependency>
    <groupId>jakarta.inject</groupId>
    <artifactId>jakarta.inject-api</artifactId>
    <version>2.0.1</version>
</dependency>

@Inject

按类型注入,语义与 @Autowired 基本一致,但没有 required 属性。

java 复制代码
@Service
public class UserService {
    @Inject
    private UserDao userDao;
}

@Named

配合 @Inject 按名称指定:

java 复制代码
@Service
public class OrderService {
    @Inject
    @Named("aliPayService")
    private PaymentService paymentService;
}

也可以标注在类上指定 Bean 名称:

java 复制代码
@Named("aliPayService")
public class AliPayService implements PaymentService { }

@Inject 和 @Named 同样由 AutowiredAnnotationBeanPostProcessor 处理,Spring 对它们提供了完整支持。

八、注入注解的底层处理链

所有注入注解最终都汇聚到两个 BeanPostProcessor:

复制代码
AutowiredAnnotationBeanPostProcessor
├── @Autowired
├── @Value
└── @Inject

CommonAnnotationBeanPostProcessor
├── @Resource
├── @PostConstruct
└── @PreDestroy

处理时机在 Bean 生命周期的属性填充阶段 (populateBean()),由 postProcessProperties() 方法完成注入。

简化流程:

java 复制代码
// AutowiredAnnotationBeanPostProcessor
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    // 1. 扫描字段和方法上的 @Autowired、@Value、@Inject
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    // 2. 逐个解析依赖并注入
    metadata.inject(bean, beanName, pvs);
    return pvs;
}

metadata.inject() 内部通过 DefaultListableBeanFactory.resolveDependency() 查找匹配的 Bean,再通过反射设置字段值或调用方法。

九、四种注入方式对比

对比维度 构造器注入 Setter 注入 字段注入 方法参数注入
推荐程度 ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐
依赖不可变 ✅ final ❌ ❌ ✅
空指针风险 无 有 有 无
单元测试 容易 中等 困难 容易
循环依赖 不支持 支持 支持 不支持
Spring 官方推荐 ✅ ⚠️ ❌ ✅

Spring 官方推荐构造器注入,原因:

  • 依赖不可变,用 final 修饰
  • 对象创建时依赖完全就绪
  • 便于单元测试,直接传入 Mock
  • 依赖过多时构造方法参数过长,能及时暴露设计问题

十、常见问题

1. 同类型多 Bean 注入失败

复制代码
NoUniqueBeanDefinitionException: expected single matching bean but found 2

解决方案:用 @Qualifier 指定名称,或用 @Primary 标记首选。

2. @Autowired 注入 null

  • 检查类是否被 Spring 管理
  • 检查包是否在 @ComponentScan 范围内
  • 检查是否设置了 required = false

3. 构造器注入循环依赖

java 复制代码
@Service
public class AService {
    public AService(BService b) { }
}

@Service
public class BService {
    public BService(AService a) { }
}

启动时报 BeanCurrentlyInCreationException。构造器注入无法解决循环依赖,应通过重构消除,或改用字段注入。

4. @Resource 找不到 Bean

@Resource 默认按名称匹配,字段名必须与 Bean 名称一致。不一致时需指定 name。

5. @Value 注入失败

复制代码
Could not resolve placeholder 'app.name'

检查配置文件中是否有对应的键,或设置默认值 ${app.name:default}。

十一、总结

维度 核心要点
核心注解 @Autowired(按类型)、@Resource(按名称)、@Inject(按类型)
辅助注解 @Qualifier(指定名称)、@Primary(标记首选)
配置注入 @Value(简单类型和配置值)
推荐方式 构造器注入
底层处理 AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor
处理时机 Bean 生命周期的属性填充阶段
匹配优先级 @Qualifier > @Primary > 按类型 > 按名称

Spring 的注入注解体系围绕"按类型"和"按名称"两条主线展开。@Autowired 和 @Inject 默认按类型,@Resource 和 @Named 默认按名称,@Qualifier 和 @Primary 用于解决歧义。理解每个注解的匹配规则和底层处理机制,能帮你在实际开发中做出正确的选择,避免注入失败和循环依赖等问题。

相关推荐
摇滚侠1 小时前
《On Java 中文版 基础卷》阅读笔记 安装 Java 和本书示例 02
java·开发语言·笔记
许彰午1 小时前
53-审计三表
java·低代码·架构
重生之小比特1 小时前
【C++进阶】红黑树的实现
java·开发语言·c++
程序猿乐锅2 小时前
【黑马点评 | 第九篇】秒杀优化-异步实现
java·spring boot·redis·后端·spring·中间件·mybatis
Wx-bishekaifayuan2 小时前
springboot户外登山社交小程序19787-计算机课程设计、毕业设计
spring boot·后端·python·spring·elasticsearch·django·课程设计
FYKJ_20103 小时前
springboot助农产品销售商城05829-计算机课程设计、毕业设计
java·spring boot·python·mysql·spark·django
user_admin_god5 小时前
第 10 篇:拼上下文与生成——预算、边界与防幻觉
java·人工智能·spring boot·语言模型
用户3126874877205 小时前
GC 算法与垃圾收集器演进:从 Serial 到 ZGC
java
天天被压力6 小时前
【别再到处找免费股票数据API了:官方204个接口,32篇一次讲透 #02】涨停跌停与特色股池:5类股池接口一次拉全
java·人工智能·python