从零开始 Spring Boot 38:Lombok 与依赖注入
图源:简书 (jianshu.com)
在之前的文章中,我详细介绍了 Lombok 的用法,考虑到在 Spring 中使用依赖注入(DI)是如此的频繁,因此有必要讨论使用 Lombok 时可能对依赖注入造成的影响。
我们都知道,Spring 中的依赖注入分为三种情况:
- 通过属性进行依赖注入。
- 通过构造器进行依赖注入。
- 通过 Setter 进行依赖注入。
通过属性进行依赖注入并不会影响我们使用 Lombok,比如:
@RestController
@RequestMapping("/hello")
@Validated
@AllArgsConstructor
public class HelloController {
@Autowired
private FibonacciService fibonacciService;
@GetMapping
public Result<?> hello(@RequestParam @Min(1) @NotNull Integer n) {
int fibonacci = fibonacciService.fibonacci(n);
return Result.success(fibonacci);
}
}
**无论我们有没有使用构造器注入,用@Autowired
标记的属性都会在 bean 实例创建后被 Spring 通过反射来完成注入。**因此实际上这里 Lombok 的@AllArgsConstructor
注解生成的构造器并不会对属性fibonacciService
的注入产生影响。
构造器注入
下面我们看构造器注入时的情形。
一般的,我们可以将属性设置为final
,并借助 Lombok 的@RequiredArgsConstructor
注解对需要的属性进行构造器注入:
@RestController
@RequestMapping("/hello")
@Validated
@RequiredArgsConstructor
public class HelloController {
private final FibonacciService fibonacciService;
@GetMapping
public Result<?> hello(@RequestParam @Min(1) @NotNull Integer n) {
int fibonacci = fibonacciService.fibonacci(n);
return Result.success(fibonacci);
}
}
我们知道,在 Spring 的后期版本中,只有一个构造器的情况下是不需要用@Autowired
来标记构造器的,Spring 默认会使用这个唯一的构造器进行注入。而在这个示例中,@RequiredArgsConstructor
会为我们创建一个包含所有final
的非静态属性的构造器,而 Spring 将用这个构造器完成注入。
我们看到,只有一个构造器时(无论是自己编写的还是 Lombok 自动生成的),都不会产生影响,但如果有多个构造器呢?
看下面这个示例:
@RestController
@RequestMapping("/hello")
@Validated
@RequiredArgsConstructor
public class HelloController {
private final FibonacciService fibonacciService;
private final Integer n;
@Autowired
public HelloController(FibonacciService fibonacciService) {
this(fibonacciService, 10);
}
@GetMapping
public Result<?> hello(@RequestParam String n) {
int numN;
if ("null".equals(n)) {
numN = this.n;
} else {
numN = Integer.parseInt(n);
}
if (numN < 1) {
return Result.fail("hello.input.invalid", "n 不能小于 1");
}
int fibonacci = fibonacciService.fibonacci(numN);
return Result.success(fibonacci);
}
}
在这个示例中,实际上字节码中会有两个构造器,一个是我们自己编写的有一个参数的构造器,另一个是 Lombok 生成的有两个参数的构造器。
注意,在自己编写的构造器中,我们使用
this(...)
调用了 Lombok 创建的构造器。
此时如果我们想通过构造器对属性fibonacciService
进行注入,就需要告诉 Spring 框架该用哪个构造器完成注入。比较简单的是,如果要用于注入的构造器是我们自己编写的(就像示例中的),我们只需要使用@Autowired
标记相应的构造器即可。
如果是反过来,就会有一点麻烦:
@RestController
@RequestMapping("/hello")
@Validated
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class HelloController {
private final FibonacciService fibonacciService;
private Integer n = 10;
public HelloController(FibonacciService fibonacciService, Integer n) {
this.fibonacciService = fibonacciService;
this.n = n;
}
// ...
}
注意,这里的属性
n
被修改为非final
的。
现在,@RequiredArgsConstructor(onConstructor = @__(@Autowired))
将在HelloController
类的字节码中生成一个带@Autowired
标记的构造器用于注入:
@RestController
@RequestMapping({"/hello"})
@Validated
public class HelloController {
// ...
@Autowired
public HelloController(final FibonacciService fibonacciService) {
this.fibonacciService = fibonacciService;
}
}
也就是说,我们可以在 Lombok 用于生成构造器的相应注解中,通过onConstructor
属性来指定一系列注解,让 Lombok 在生成构造器时在构造器上添加上这些注解。
这种写法(
@__(@Autowired))
)有些奇怪,实际上@__
是一个并不存在的注解,可以用它来包裹一个注解列表(逗号分隔)来为构造器指定多个用于生成时添加的注解。
Setter 注入
我们知道,在使用 Setter 注入时,必须要使用@Autowired
注解。同样的,可以用类似的方式让 Lombok 生成 Setter 时添加上相应的注解:
@RestController
@RequestMapping("/hello")
@Validated
public class HelloController {
@Setter(onMethod = @__(@Autowired))
private FibonacciService fibonacciService;
private final Integer n = 10;
@GetMapping
public Result<?> hello(@RequestParam String n) {
// ...
}
}
The End,谢谢阅读。
这篇文章相对来说简短很多,算是对之前的文章的一个补充。
本文所有的示例代码可以通过这里获取。
参考资料
- Constructor Injection in Spring with Lombok | Baeldung