IoC和DI是Spring中最重要的两个概念,其中IoC(InversionofControl)是控制反转的思想,DI(DependencyInjection)是其(IoC)的具体实现。那么DI实现依赖注入有多少种方式呢?这些注射方法有何不同?接下来,我们一起来看看吧。0.概述Spring中实现依赖注入的常用方式有3种:FieldInjection;二传手注射;构造函数注入。它们的具体用途和优缺点分析如下。1.属性注入属性注入是日常开发中最熟悉也是使用最多的注入方式。其实现代码如下:@RestControllerpublicclassUserController{//属性对象@AutowiredprivateUserServiceuserService;@RequestMapping("/add")publicUserInfoadd(Stringusername,Stringpassword){returnuserService.add(username,password);}}1.1优势分析属性注入最大的优势就是实现简单,使用方便。只需要给变量加上注解(@Autowired),不需要new对象就可以直接得到注入的对象(这就是DI的功能和魅力),所以它的优点是简单易用。1.2劣势分析然而,属性注入虽然使用方便,但也存在很多问题。连编译器Idea都会提醒你“不推荐这种注入方式”。Idea的提示信息如下:属性注入的缺点主要有以下三个:功能问题:无法注入一个不可变对象(最后修改的对象);通用性问题:只能适配IoC容器;设计原则问题:更容易违反单一的设计原则。接下来,让我们一一了解。缺点一:功能性问题不可变对象(finally修饰的对象)不能使用属性注入来注入,如下图:原因也很简单:在Java中,final对象(不可变)要么直接赋值,要么在构造中赋值method因此,使用属性注入final对象时,不符合Java中final的使用规范,无法注入成功。PS:如果要注入不可变对象,怎么实现呢?使用以下构造函数进行注入。缺点二:通用性使用属性注入的方法只适用于IoC框架(容器)。如果将属性注入的代码移植到其他非IoC框架中,代码就会失效,所以属性注入的普适性不是很好。缺点三:设计原理问题是使用属性注入。因为使用起来非常简单,所以开发者很容易将多个对象同时注入到一个类中。这些对象的注入有必要吗?是否符合程序设计中的单一职责原则?成为一个问题。但可以肯定的是,注入实现越简单,被误用的概率就越大,因此违反单一职责原则的概率就越大。注意:这里强调的是有可能违反设计原则(单一职责),不一定违反设计原则。两者有着本质的区别。2、Setter注入Setter注入实现代码如下:@RestControllerpublicclassUserController{//Setter注入privateUserServiceuserService;@AutowiredpublicvoidsetUserService(UserServiceuserService){this.userService=userService;}@RequestMapping("/add")publicUserInfoadd(Stringusername,Stringpassword){returnuserService.add(username,password);}}优缺点分析从上面的代码可以看出,setter注入要比属性注入麻烦的多。如果说Setter注入有什么优点的话,首当其冲的就是它完全符合单一职责的设计原则,因为每个Setter只针对一个对象。但其缺点也很明显。它的缺点主要体现在以下两点:不可变对象(最终修改的对象)不能被注入;可以修改注入的对象。接下来,让我们一一了解。缺点一:不可变对象无法注入。使用Setter注入仍然无法注入不可变对象。比如下面的注入就会报错:缺点2:注入的对象是可以修改的。setter注入提供了一个setXXX方法,也就是说你可以随时随地使用。地方,注入的对象是通过调用setXXX方法改变的,所以Setter注入的问题是注入的对象随时可能被修改。3、构造器注入构造器注入是Spring官方从4.x开始推荐的注入方式。其实现代码如下:@RestControllerpublicclassUserController{//构造函数注入privateUserServiceuserService;@AutowiredpublicUserController(UserServiceuserService){this.userService=userService;}@RequestMapping("/add")publicUserInfoadd(Stringusername,Stringpassword){returnuserService.add(username,password);}}当然,如果当前类只有一个构造函数,那么@Autowired也可以省略,所以上面的代码也可以这样写:@RestControllerpublicclassUserController{//constructorinjectionprivateUserServiceuserService;publicUserController(UserServiceuserService){this.userService=userService;}@RequestMapping("/add")publicUserInfoadd(Stringusername,Stringpassword){returnuserService.add(username,password);}}优势分析构造函数注入相比前面两种注入方式,它可以注入不可变对象,而且只会执行一次,不会出现像Setter注入那样被注入的对象随时被修改的情况。它有以下四个优点:可以注入不可变对象;注入的对象不会被修改;注入的对象将被完全初始化;通用性更好。接下来,让我们一一了解。优势一:注入不可变对象不可变对象可以使用构造函数注入来注入,如下代码所示:优势二:注入的对象不会被修改对象只创建一次,所以不存在随时修改(调用)注入的对象。优势三:完全初始化因为依赖对象是在构造方法中执行的,而构造方法是在对象创建之初就执行的,所以注入的对象在使用前会被完全初始化,这也是构造方法的优点之一注射一。优势四:更好的通用性构造函数注入不同于属性注入。构造函数注入可以应用于任何环境。无论是IoC框架还是非IoC框架,构造函数注入的代码都是通用的,因此其通用性更好。依赖注入的三种常见实现方式是:属性注入、setter注入和构造函数注入。其中,属性注入是最简单的写法,所以在日常项目中使用频率最高,但通用性不好;而Spring官方推荐构造函数注入,可以注入不可变对象,通用性更好。如果要注入可变对象,可以考虑使用Setter注入。References&AcknowledgementsSpring官方文档,自己判断,别人判断,得失用数字算。公众号:Java面试真题分析面试合集:https://gitee.com/mydb/interview
