参考 :
面试突击77:Spring 依赖注入有几种?各有什么优缺点? - 掘金
目录
更加简单地从Spring中取出Bean对象(超级重要)
属性注入
属性注入的优点和缺点
setter注入
Setter注入的优缺点
构造方法注入
注意事项
构造方法注入的优缺点
官方建议
将Bean对象更加简单地从Spring中取出来,有三种方式,分别为 属性注入, setter注入,构造方法注入
我们可以回忆一下之前是怎么拿到一个对象的.
最初我们在没有学Spring之前就是直接new一个对象
学了Spring之后呢... 我们先要创建一个Spring上下文对象(context),通过context.getBean()方法指定id,类型来从Spring中取出Bean对象..
到现在我们就可以使用更加简单地方式来从Spring中取出Bean对象了.
我们分别来讲一下属性注入,Setter注入,构造方法注入 并分析出三种方式的优缺点,以及Spring官方推荐我们使用哪种呢 ?
首先我来代码演示一下怎么才是属性注入,代码层次上是怎么写的呢..
我们这里代码演示是 我们在UserController类里面要使用UserService类....
//和前端进行交互的第一层(主要验证前端传递过来的参数-->进行校验和验证==>相当于安检的作用) @Controller public class UserController { //我们这里假设要在UserController类里面使用UserService,前提是UserService已经存放到Spring中了(加了类注解了) @Autowired //使用一个AutoWired这样的一个注解---->自动装配的意思 private UserService userService;//UserService成员属性 public void sayHello(){ System.out.println("do UserController!!!"); userService.doUserService();//调用UserService的方法 } }
我们看能不能成功呢 ?
看来是没有问题的..
注解真神奇..我们来见一下@AutoWired
AutoWired这个注解就是自动装配的意思,AutoWired来自于Spring.
由于我们的属性是在Spring中的(通过类注解),当在属性上面这加了AutoWired注解(就相当于功能的声明,要将存到spring中的这个属性,从spring中读取出来然后赋值给当前这个属性(自动装配))之后,Spring框架就会自动的将这个属性动态的注入到当前的类中.这就是属性注入
现在还有一个问题,就是我们已经知道使用注解@AutoWired就可以从Spring中读取Bean,那为啥启动类中获取Bean的方式还是使用getBean呢,为啥呢?
可以再启动类通过属性注入的方式,得到Bean对象可以么 ?
Non-static field 'userController' cannot be referenced from a static context
非静态字段“userController”不能从静态上下文引用
这也就说明了原因,由于启动类中的main方法->是static的,所以不能在静态方法中使用
再具体的原因就是因为静态方法执行的时机比Spring注入的时机要早,也就是说我在启动类使用这个属性的时候,spring框架还没有把这个属性注入到当前类中
如果你还不行的话,那把UserController加上static,随然不报错了,但由于spring执行注入的时机比较晚,所以会发生NullPointerException 空指针异常
属性名可以随便起
属性注入的这个属性由于是在Spring中的,所以是可以在其他类里面重复对这个属性进行属性注入的.
这个属性名字是可以随便起的(AutoWired是先去找类型,再去找名称->这个后面讲解)
优点 :
缺点 :
为啥呢 ?
所以,是因为不满足Java的规范
啥是单一职责原则呢 ?
单一职责原则的核心思想 : 一个类最好只做一件事,只有一个引起它变化的原因
换句话说,类只有单一功能,不要为类实现过多的功能点,以保证实体只有一个引起职责变化的原因
一个类只做一件事,要么做A事情,要么做B事情.只做一件事情
为什么说属性注入更容易违背单一职责原则呢 ?
啥是setter注入呢?
setter注入就是使用我们Java的setter方法配合注解将Bean注入到当前类中.
我们来演示一下,怎么才算是属性注入.
优点 :
因为setter方法的特性,一个setter方法只对应一个对象,不会有注入多个对象的可能性,所以满足单一设计/职责原则
缺点 :
还是与属性注入的解释是一样的,原因就是JDK是我们最底层的框架,Spring作为上层,要基于JDK/Java的,所以要满足Java的规范,java规定,被final修饰的属性1.在定义的时候进行赋值2.在构造方法内部进行赋值
由于setter方法是可以被多次调用的,有修改的风险,所以注入的对象就可能被修改.
构造方法注入就是利用构造方法将对象/Bean注入到当前类中
我来演示一下 :
对于构造方法注入也是官方最推荐我们使用的.(但悲哀的是我们依然会使用属性注入->因为写法简单,即使官方推荐用构造方法注入,但是官方自己的原码也是在使用属性注入.)
如果使用了构造方法注入,在类加载的时候,看到有在构造方法上加个注解,就会从Spring中取出该Bean,注入到当前类中.
构造方法注入在有一个构造方法的时候,可以不加注解
为啥呢 ?
原因就是在Spring设计的时候,当使用构造方法注入的时候并且只有一个构造方法,Spring就会将对象注入到当前类中,给这个属性进行赋值.就可以省略注解AutoWired-->但必须是只有一个构造方法,才会符合
存在多个构造方法的时候,还可以省略注解么?
如果存在多个构造方法,他就不知道给哪一个构造方法注入了,并不知道到底执行哪个构造方法,这就不能注入,所以下面在调用的时候就会触发空指针异常.
这就好比,你班级有一个张三,老师在点名的时候,只有它会站起来回答问题,但是当你班级有两个名字为张三的时候,老师叫张三回答为题,两个张三都懵了,你到底叫谁呢??
所以当存在多个构造方法的时候必须要对要加注解.这样Spring就知道你到底执行哪个构造方法,在哪一个构造方法中注入Bean.
构造方法中一次可以注入多个对象么?
我们可以演示一下
构造方法注入是可以一次注入多个对象的
优点 :
构造方法最牛,上面的缺点,都是俺构造方法的优点.
就比如,属性注入和setter注入都不能解决注入final修饰的属性问题,那对于构造方法注入就能够解决
为什么构造方法可以注入final修饰的属性呢?
原因还是一样的,因为满足Java的规范,被final修饰的属性 一个是定义的时候就进行赋值,一个是在构造方法内部进行赋值.满足第二条,所以可以注入final修饰的属性.
构造方法注入 注入的对象不会被修改,因为构造方法只会执行一次.
因为依赖注入是在构造方法内部执行的,而构造方法又是在类起初创建的时候就执行的,所以会被完全初始化
构造方法注入因为基于java的,JDK是最底层框架,所以无论在哪一个容器/框架都可以适用
缺点 :
在Spring4.2之前推荐的注入用法就是setter注入,因为setter注入更加符合单一设计/职责原则
在Spring4.2之后官方就推荐使用构造方法注入的方式(因为它的优点).如果要传入太多参数就需要考虑单一设计原则问题了.
但是我们在开发的时候依然会使用属性注入的方式,因为写法很简便.