Field injection is not recommended
在使用spring框架中的依赖注入注解@Autowired的时候,IDEA报了一个警告
被警告的代码如下
@Autowired
private PayLogService payLogService;
警告的内容如下
Field injection is not recommended // 使用变量的以来注入是不推荐的
Spring官方给出解决方案是这样的
Inspection info: Spring Team recommends: "Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies".
翻译过来就是==总是在您的bean中使用构造函数建立依赖注入。总是使用断言强制依赖==
我们直接使用Alt + Enter后代码变成了这样
private final PayLogService payLogService;
public PayLogController(PayLogService payLogService) {
this.payLogService = payLogService;
}
可以看出,令人心烦的大波浪已经没有了
可话又说回来,为什么Spring官方团队推荐我们这样做?
在讨论上面的问题之间,我们先来了解一下Spring中以来注入的方式
Spring中依赖注入的方式有三种:
- 基于构造函数的依赖注入
- 基于setter的依赖注入
- 基于字段的依赖注入
其中基于字段的依赖注入也就是使用==@Autowired==注入依赖的方式是我们最最常用的方式,但是IDEA或者其他静态代码分析工具会给出提示信息,不推荐使用。
基于构造函数的依赖注入
在基于构造函数的依赖注入中,类构造函数被标注为**@Autowired**,并包含了许多与要注入的对象相关的参数。
@Component
public class ConstructorBasedInjection {
private final InjectedBean injectedBean;
@Autowired
public ConstructorBasedInjection(InjectedBean injectedBean) {
this.injectedBean = injectedBean;
}
}
然后在spring官方文档中,@Autowired注解也是可以省去的。
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on a MovieFinder
private MovieFinder movieFinder;
// a constructor so that the Spring container can inject a MovieFinder
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
基于构造函数注入的主要优点是可以将需要注入的字段声明为final, 使得它们会在类实例化期间被初始化,这对于所需的依赖项很方便。
基于Setter的依赖注入
在基于setter的依赖注入中,setter方法被标注为**@Autowired**。一旦使用无参数构造函数或无参数静态工厂方法实例化Bean,为了注入Bean的依赖项,Spring容器将调用这些setter方法。
@Component
public class SetterBasedInjection {
private InjectedBean injectedBean;
@Autowired
public void setInjectedBean(InjectedBean injectedBean) {
this.injectedBean = injectedBean;
}
}
和基于构造器的依赖注入一样,在官方文档中,基于Setter的依赖注入中的**@Autowired**也可以省去。
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on the MovieFinder
private MovieFinder movieFinder;
// a setter method so that the Spring container can inject a MovieFinder
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
基于属性的依赖注入
在基于属性的依赖注入中,字段/属性被标注为**@Autowired**。一旦类被实例化,Spring容器将设置这些字段。
@Component
public class FieldBasedInjection {
@Autowired
private InjectedBean injectedBean;
}
Java变量的初始化顺序:静态变量或静态语句块–>实例变量或初始化语句块–>构造方法–>@Autowired
相比较而言:
**优点:**变量方式注入非常简洁,没有任何多余代码,非常有效的提高了java的简洁性。即使再多几个依赖一样能解决掉这个问题。
**缺点:**不能有效的指明依赖。相信很多人都遇见过一个bug,依赖注入的对象为null,在启动依赖容器时遇到这个问题都是配置的依赖注入少了一个注解什么的,然而这种方式就过于依赖注入容器了,当没有启动整个依赖容器时,这个类就不能运转,在反射时无法提供这个类需要的依赖。
在使用set方式时,这是一种选择注入,可有可无,即使没有注入这个依赖,那么也不会影响整个类的运行。
在使用构造器方式时已经显式注明必须强制注入。通过强制指明依赖注入来保证这个类的运行。
另一个方面:
依赖注入的核心思想之一就是被容器管理的类不应该依赖被容器管理的依赖,换成白话来说就是如果这个类使用了依赖注入的类,那么这个类摆脱了这几个依赖必须也能正常运行。然而使用变量注入的方式是不能保证这点的。
既然使用了依赖注入方式,那么就表明这个类不再对这些依赖负责,这些都由容器管理,那么如何清楚的知道这个类需要哪些依赖呢?它就要使用set方法方式注入或者构造器注入。
总结下:
在类被加载运行的时候就必须给他的依赖就是强制依赖,构造方法型依赖注入就是强制性,你初始化这个类就要给我这个依赖,选择依赖就是它在初始化的时候IOC不会给你注入,直到你使用到这个依赖时它才会把这个依赖给这个类,比如当你调用这个依赖的方法时才会给你把这个依赖给你注入进去,变量注入和set方法注入都是选择依赖
变量方式注入应该尽量避免,使用set方式注入或者构造器注入,这两种方式的选择就要看这个类是强制依赖的话就用构造器方式,选择依赖的话就用set方法注入。