在IDEA中使用@Autowired注解,会提示如下内容,那么为什么会出现以下提示呢?下面我们进一步分析分析

原因分析

关于这个问题,答案其实比较统一,通俗易懂其实也很容易理解。

  • 初始化问题

Java初始化类的顺序:父类静态字段>父类静态代码块>子类静态字段>子类静态代码块>父类成员变量>父类结构代码块>父类构造函数> 子类成员变量 > 子类构造代码块 > 子类构造函数。

自动装配注入必须在子类构造函数之后排队。SpringIOC不会判断依赖bean是否为null,JVM编译时不会有问题,但如果使用不当,运行起来可能会出现空指针异常。

  • 容易过度依赖IOC

@Autowired由 Spring 提供,而@Resource由 JSR-250 提供,这是一个 Java 标准。前者会警告,后者不会,因为前者导致应用程序和框架之间存在强绑定。如果换成别的IOC框架,就无法注入成功。其实对于这方面,我觉得大部分情况下不会有什么问题。

  • 其他

我在网上看到了一些其他方面的总结,比如:依赖太多但不够明显,违反了单一职责原则;immutable objects不能像constructors之类的注入,这类问题需要结合个人实际开发判断。

对于@Autowired的使用,虽然强绑定了业务代码和框架,但是字段注入确实大大简化了代码。追求完全松耦合其实过于理想化,在实际使用中应该追求一个平衡,否则过度追求松耦合得不偿失

替换方案

除了使用@Autowired,也可以使用@Resource替换@Autiwired方法就是其中之一。只需要修改一个注解,可以看到,修改后,警告信息就没有了

下面再介绍几种其它方式

set方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RestController
public class TestAutoController {

ITestAutoService testAutoService;

/*
* 基于set注入
* */
@Autowired
public void setTestAutoService(ITestAutoService testAutoService) {
this.testAutoService = iTestAutoService;
}

@GetMapping("/getTestAtuo")
public Result<TestAutoDto> getAuto() {
return testAutoService.getAuto();
}
}

该方法也使用了@Autowired注解,但作用于成员变量的Setter函数,而不是像Fied注入那样作用于成员变量

构造器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
public class TestStructController {

ITestStructService testStructService;

/*
* 基于构造方法注入
* */
public TestStructController(ITestStructService testStructService) {
this.testStructService = iTestStructService;
}

@GetMapping("/getTestStruct")
public Result<TestStructDto> getStruct() {
return testStructService.getStruct();
}
}

它的优点是使用构造函数注入,这需要对象创建的顺序,它将避免循环依赖问题,是比较可靠的方法

构造器的简化版(lombok)

引入maven依赖

1
2
3
4
5
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>

然后,我们可以在创建时使用@RequiredArgsConstructor注释,这将帮助我们创建一个构造函数,final关键字是必不可少的。

1
2
3
4
5
6
7
8
9
10
11
12
13
@RestController
@RequiredArgsConstructor
public class TestArgsController {
/*
* 用@RequiredArgsConstructor注解,也可以应用于service层
* */
private final ITestArgsService testArgsService;

@GetMapping("/getArgs")
public Result<?> getArgs() {
return testArgsService.getArgs();
}
}

总结

在使用中,使用构造的方法是比较可行的,而有了lombok,实际上可以非常轻松地实现。