一、前言
Spring框架核心是IOC与AOP,在代码中我们常会发现在idea等编译器中将鼠标放在@Autowired上会提示Field injection is not recommended,意思是Spring不推荐直接注入
二、常见的三种注入方式
1、第一种我们经常用的——field注入
例:
@RestController
public class UserController {
@Autowired //field注入方式
private UserService userService;
//简单举例,获取用户列表
public List<User> getList() {
return userService.getList();
}
}
这种方式是程序猿门普遍使用的也是最受欢迎的,为什么呢?
- 方式简单,直接在字段上加上@Autowired即可完成
- 使得简洁明了,干净利落
2、spring官方推荐方式——构造器注入
例:
@RestController
public class UserController {
private UserService userService;
@Autowired //构造器注入
public UserController(UserService userService) {
this.userService = userService;
}
//简单举例,获取用户列表
public List<User> getList() {
return userService.getList();
}
}
如果有多个注入参数,则累加就行,例:
@RestController
public class UserController {
private UserService userService;
private LogService logService;
@Autowired //构造器注入
public UserController(UserService userService, LogService logService) {
this.userService = userService;
this.logService = logService;
}
//使用方式相同
public Log selete() {
return logService.selectOne(); //获取单条数据
}
}
重点来了
Spring4.x版本中推荐的注入方式就是这种,对比第一种方式而言此方式看上去就没有那么简洁优美的样子,特别适当注入变多时就会显得代码臃肿,但是Spring官方推荐自有它的好处。下面我们就来看看官方说法:
1、Spring文档中是这样阐述的:
Constructor-based DI is accomplished by the container invoking a constructor with a number of arguments, each representing a dependency. Calling a static
factory method with specific arguments to construct the bean is nearly equivalent, and this discussion treats arguments to a constructor and to a static
factory method similarly.
翻译过来就是:基于构造函数的 DI由容器调用具有多个参数的构造函数来完成,每个参数表示一个依赖项。调用static
具有特定参数的工厂方法来构造bean几乎是等效的,本讨论同样处理构造函数和static
工厂方法的参数。
The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null
. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.
译:Spring团队通常提倡构造函数注入,因为它使应用程序组件可以实现为不可变对象,并确保不需要所需的依赖项null
。此外,构造函数注入的组件始终以完全初始化的状态返回到客户端(调用)代码。作为旁注,大量的构造函数参数是一个糟糕的代码气味,暗示该类可能有太多的责任,应该重构以更好地解决关注点的正确分离。
依赖性解决过程
容器执行bean依赖性解析,如下所示:
-
使用
ApplicationContext
描述所有bean的配置元数据创建和初始化。可以通过XML,Java代码或注释指定配置元数据。 -
对于每个bean,如果使用的是依赖于普通构造函数的,那么它的依赖关系将以属性,构造函数参数或static-factory方法的参数的形式表示。实际创建 bean 时,会将这些依赖项提供给bean 。
-
每个属性或构造函数参数都是要设置的值的实际定义,或者是对容器中另一个bean的引用。
-
作为值的每个属性或构造函数参数都从其指定格式转换为该属性或构造函数参数的实际类型。默认情况下,Spring能够转换成字符串格式提供给所有的内置类型,比如数值
int
,long
,String
,boolean
,等。
下面我们看一下第三种方式
3、setter注入
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
@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
该ApplicationContext
支架构造和基于setter方法的DI为它所管理的beans。在通过构造函数方法注入了一些依赖项之后,它还支持基于setter的DI。您可以以a的形式配置依赖项,并将BeanDefinition
其与PropertyEditor
实例结合使用,以将属性从一种格式转换为另一种格式。然而,大多数Spring用户不直接与这些类(即,编程),而是用XML bean
定义,注释组件(即与注释类@Component
,@Controller
等等),或@Bean
在基于Java的方法@Configuration
类。然后,这些源在内部转换为实例BeanDefinition
并用于加载整个Spring IoC容器实例。
基于构造函数或基于setter的DI?
由于您可以混合基于构造函数和基于setter的DI,因此将构造函数用于强制依赖项和setter方法或可选依赖项的配置方法是一个很好的经验法则。请注意, 在setter方法上使用@Required注释可用于使属性成为必需的依赖项。
Spring团队通常提倡构造函数注入,因为它使应用程序组件可以实现为不可变对象,并确保不需要所需的依赖项null
。此外,构造函数注入的组件始终以完全初始化的状态返回到客户端(调用)代码。作为旁注,大量的构造函数参数是一个糟糕的代码气味,暗示该类可能有太多的责任,应该重构以更好地解决关注点的正确分离。
Setter注入应主要仅用于可在类中指定合理默认值的可选依赖项。否则,必须在代码使用依赖项的任何位置执行非空检查。setter注入的一个好处是setter方法使该类的对象可以在以后重新配置或重新注入。因此,通过JMX MBean进行管理是二次注入的一个引人注目的用例。
使用对特定类最有意义的DI样式。有时,在处理您没有源的第三方类时,会选择您。例如,如果第三方类没有公开任何setter方法,那么构造函数注入可能是唯一可用的DI形式。
三、总结
使用构造器注入的好处:
保证依赖不可变(final关键字)
保证依赖不为null(省去了我们对其检查)