为什么我的 Spring @Autowired 字段为 null?

为什么我的 Spring @Autowired 字段为 null?

技术背景

在Spring应用开发中,@Autowired 注解是实现依赖注入的常用方式,它可以自动将所需的Bean注入到相应的字段中。然而,有时候会遇到使用 @Autowired 注解的字段为 null 的情况,这会导致 NullPointerException 异常,影响程序的正常运行。

实现步骤

1. 检查对象创建方式

Spring IoC容器负责管理Bean的生命周期和依赖注入。如果使用 new 关键字手动创建对象,Spring无法对其进行配置和注入依赖。

错误示例

1
2
3
4
5
6
7
8
9
@Controller
public class MileageFeeController {
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
MileageFeeCalculator calc = new MileageFeeCalculator(); // 手动创建对象
return calc.mileageCharge(miles);
}
}

正确示例:使用 @Autowired 注解让Spring自动注入对象。

1
2
3
4
5
6
7
8
9
10
11
12
@Controller
public class MileageFeeController {

@Autowired
private MileageFeeCalculator calc;

@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}

2. 确保类是Spring Bean

要让Spring对类进行管理和依赖注入,类必须是Spring Bean。可以使用 @Component@Service@Repository@Controller 等注解将类标记为Spring Bean。

1
2
3
4
5
6
7
8
9
10
@Service
public class MileageFeeCalculator {

@Autowired
private MileageRateService rateService;

public float mileageCharge(final int miles) {
return (miles * rateService.ratePerMile());
}
}

3. 配置组件扫描

确保Spring能够扫描到标记为Bean的类。可以使用 @ComponentScan 注解指定要扫描的包。

1
2
3
4
5
@Configuration
@ComponentScan(basePackages = "com.chrylis.example")
public class AppConfig {
// 配置类
}

4. 检查注解导入的包

确保使用的注解(如 @Autowired@Inject@Service 等)导入的是正确的包。

1
import org.springframework.beans.factory.annotation.Autowired; // 正确的导入

5. 避免在构造函数中使用未初始化的依赖

在构造函数中,@Autowired 注解的字段可能还未初始化。可以使用 @PostConstruct 注解在Bean初始化后执行一些操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
public class MyComponent {
@Autowired
ComponentDAO dao;

public MyComponent() {
// dao 在这里为 null
}

@PostConstruct
public void init() {
// dao 在这里已初始化
}
}

核心代码

示例代码结构

  • MileageFeeController:控制器类,处理HTTP请求。
  • MileageFeeCalculator:服务类,计算里程费用。
  • MileageRateService:服务类,提供每英里的费率。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// MileageFeeController.java
@Controller
public class MileageFeeController {

@Autowired
private MileageFeeCalculator calc;

@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}

// MileageFeeCalculator.java
@Service
public class MileageFeeCalculator {

@Autowired
private MileageRateService rateService;

public float mileageCharge(final int miles) {
return (miles * rateService.ratePerMile());
}
}

// MileageRateService.java
@Service
public class MileageRateService {
public float ratePerMile() {
return 0.565f;
}
}

最佳实践

  • 使用构造函数注入:优先使用构造函数注入,而不是字段注入,这样可以提高代码的可测试性和可读性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
public class MileageFeeCalculator {

private final MileageRateService rateService;

@Autowired
public MileageFeeCalculator(MileageRateService rateService) {
this.rateService = rateService;
}

public float mileageCharge(final int miles) {
return (miles * rateService.ratePerMile());
}
}
  • 避免循环依赖:循环依赖会导致依赖注入失败,尽量避免在Bean之间形成循环依赖。

常见问题

1. 使用 @Configurable 注解时的问题

如果使用 @Configurable 注解让Spring对 new 创建的对象进行配置,需要进行额外的配置,如使用AspectJ编译时织入。

2. 测试类中 @Autowired 字段为 null

在测试类中,确保使用正确的注解(如 @RunWith(SpringRunner.class)@SpringBootTest)来启动Spring上下文。

1
2
3
4
5
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
// 测试代码
}

3. 静态字段注入问题

不要对静态字段使用 @Autowired 注解,因为静态字段在类加载时就已经初始化,Spring无法对其进行注入。


为什么我的 Spring @Autowired 字段为 null?
https://119291.xyz/posts/2025-04-11.why-is-my-spring-autowired-field-null/
作者
ww
发布于
2025年4月22日
许可协议