更简单的取 Bean 对象(对象装配)

一. 获取 Bean 对象

获取 Bean 对象就是把某个对象从 Spring 容器中取出来放到某个类中, 也叫做对象装配或者对象注入, 对于获取的前提都是对象存在于该容器中(对象在指定扫描包路径底下, 或者配置文件单独添加了 bean 对象注入)

二. 对象注入的三种方法

1. 属性注入

属性注入使用 @Autowired 注解, 并且不需要 new 对象
例如, 将 ServiceStudent 类注入到 ControllerStudent 类中
image.png
可以看到, 将其注入到 ControllerStudent 中时, 是不需要进行 new 对象的
image.png

获取 Bean 对象并使用, 来验证是否真的通过 @Autowried 注解将属性注入到了其中
image.png
运行结果可以看到, 成功的调用了 ServiceStudent 中的 fun( ) 方法, 成功的将 ServiceStudent 类通过 @Autowired 注解成功注入到 ControllerStudent 类中

属性注入的问题

  1. 属性注入非常的方便, 但是为什么我们在 main 方法中不直接使用 @Autowired 将 ControllerStudent 直接获取到在使用, 岂不是方便很多 ?

image.png
尝试后发现, 报错提示他不是一个静态的对象, 哪加上 static 让它变成静态对象是不是可以用了呢 ?
image.png
加了静态方法以后, 还是不行 这是为什么呢 ?
这和 JVM 的加载有关系, 静态方法的加载是在 Spring 之前的. 因此, 在对象还未被注入到 Spring 容器当中时, ControllerStudent 对象就已经被加载了, 此时从容器当中拿不到这个对象, 再去使用就会空指针异常了.

  1. 无法注入被 final 修饰的对象

image.png
可以看到的是, 当使用 @Autowired 注解去注入对象的时候, 报错提示 serviceStudent 变量无法被初始化, 原因很简单, final 的对象在创建时, 都需要进行初始化, 而此处使用 @Autowired 注解注入时, 并没有立刻给 serviceStudent 去进行初始化, 而是在执行时从容器中去获取后才赋值给它.

  1. 有违反单一性的风险

从上面可以将 ServiceStudent 类注入到 ControllerStudent 中就可以看出来, @Autowired 属性注入是一种非常简单的获取 Bean 对象的方法, 因此在使用中, 极有可能注入多次, 是否符合项目的单一性就变成了一个问题.

  1. 兼容性差

只能在 IoC 容器中使用.

属性注入的优点

从上面的注入示例看到, 属性注入用起来非常的爽, 就主打一个简单, 因此优点也是十分的明显, 就是使用简单 !

2. Setter 注入

顾名思义, 简单的理解就是 通过 set 方法将类注入对象, 需要使用 @Autowired 注解搭配
同样的, 将 ServiceStudent 类注入到 ControllerStudent 中** **
image.png
获取 Bean 对象并调用验证是否真的将 ServiceStudent 注入到 ControllerStudent 中
image.png
通过结果来看, 是成功将 ServiceStudent 类注入到了 ControllerStudent 中的

Setter 注入的问题

  1. setter 注入体现在哪里 ?

对于 Setter 注入, 不是说顾名思义就是通过 set 方法注入嘛 ? 体现在哪里了呢 ?
通过刚刚的使用可以看出, 在创建所需要的注入类对象后, 通过生成 set 方法, 将要注入的对象传入其中, 即可完成赋值

  1. @Autowired 在这里起什么作用 ?

为什么 setter 注入要用到 @Autowired 呢 ? ** **set 方法中的参数又是谁给赋值的呢 ?
对于这些疑问, 通过 @Autowired 从 Spring 中去获取到对象, 并将这个对象传入给 set 方法的参数中, 最后通过参数的形式赋值给所需要的类对象进行注入. 如果没有 @Autowired 将无法从 Spring 容器中获取对象后注入

  1. 无法注入 final 修饰的变量

和属性注入一样, 都无法将 final 修饰的变量注入其中, 原因和属性注入中是一样的
image.png

  1. 注入的对象可以修改

由于 setter 注入是一个简单的 set 方法注入, 因此在注入的类中, 可以去任意调用以及修改, 可能会存在被注入多次不同的对象
image.png
在当前要注入的 ControllerStudent 中, 可以去任意的修改注入的 serviceStudent 对象

Setter 注入的优点

  1. 非常符合单一性

由于 setter 注入是使用普通的 set 方法进行注入, 而 set 方法每次只能传一个参数, 及时要注入两个对象, 也是设置两个 set 方法, 因此它非常的符合单一性, 不会存在多份的可能性

3. 构造方法注入

构造方法注入和 Setter 注入非常的像, 只不过 setter 注入是需要体统 set 方法, 而构造方法注入提供的是构造方法. 为什么说像呢 ? **因为他们都需要搭配 @Autowired 使用 **
同样的, 将 ServiceStudent 类注入到 ControllerStudent 类中
image.png
获取 Bean 对象并调用方法进行验证
image.png
通过调用方法可以看出, 使用构造方法搭配 @Autowried 注解成功的将对象注入成功

构造方法注入的问题

  1. @Autowired 在这里起什么作用 ?

为什么 构造方法注入要用到 @Autowired 呢 ? ** **构造方法中的参数又是谁给赋值的呢 ?
对于这些疑问, 通过 @Autowired 从 Spring 中去获取到对象, 并将这个对象传入给构造方法的参数中, 最后通过参数的形式赋值给所需要的类对象进行注入. 如果没有 @Autowired 将无法从 Spring 容器中获取对象后注入

  1. 多个参数构造时, 略显臃肿

既然是构造方法注入, 那么肯定是可以同时提供多个参数的, 意味着可以同时注入多个参数, 那么会显得程序有点臃肿, 同时还应当去考虑当前类是否符合程序的单一职责的设计模式, 只实现一个功能.

构造方法的优点

  1. 通用性

带参数得构造方法,在很多的框架中都可以用, 通用性也会更好.

  1. 保证注入对象被完全初始化

因为类加载的关系, 一个类在进行加载的时候, 先进行的是实例化, 在进行初始化, 在进行初始化的时候去执行构造方法, 在执行构造方法的时候就会把依赖的对象注入到当前类当中, 在底下执行使用的时候, 该对象一定是被完全初始化了的.

  1. 注入对象不可以被修改

构造方法只能执行一次, 因此注入的对象是不可以被修改的

  1. 可以一次注入多个类

可以同时注入多个类, 虽然会让程序略显臃肿, 但在某些必要场景下, 一次传入多个会更简单
image.png
获取 Bean 对象并调用验证
image.png
可以看到, 构造方法可以同时注入多个类

  1. 可以注入 final 修饰的变量

image.png
此处 idea 提示我们, 该字段可能是 final 修饰的, 当加入 final 修饰以后, 就没有提示了
image.png
相比于前面两种注入方法, 构造方法支持注入 final 修饰的变量, 对于一些需要的情况下会更适用
**那么, 为什么构造方法就可以注入 final 修饰的对象呢 ? **
在这, 就需要去理解 final 的用法了, 对于 final 修饰的对象需要满足其中的条件之一

  • final 在使用的时候, 直接进行赋值
  • final 必须在构造方法中赋值

由于在构造方法中, final 修饰的变量已经在构造方法中进行赋值了, 因此构造方法注入可以注入 final 修饰的变量

  1. **当前类中如果只有一个构造方法, 允许不使用 @Autowired **

image.png
同样调用运行观察
image.png
可以看到的是, 当我们构造方法中只有一个构造方法时, 是可以不添加 @Autowired 注解的, 这是 Spring 官方对于该注入方法进行的单独处理.
如果有多个构造方法的时候, 不添加 @Autowired 就不行了
例如下面这段代码 :
image.png这里为什么会报错呢 ? 报错的内容是 serviceStudent 和 componentStudent 变量无法进行初始化. 为什么在添加了多个构造方法, 同时也添加了 @Autowired 注解还是会报错 ?
原因很简单, 由于这两个字段都是 final 修饰的, 需要满足两个条件中的一个, 此处为在构造方法时就需要赋值, 因此只能使用两个参数的构造方法注入, 又因为提供了其他两个的构造方法, 当去使用这两个构造方法的时候, final 修饰的字符已经被初始化了, 无法在进行初始化修改了是一个不可变的字段了.
因此在使用当中, 多个参数的构造方法注入时, 应当考虑好是否使用 final 修饰, 此处将 @Autowired 注释放在了第二个构造方法中, 注入的就是指定的第二个构造方法的参数, 因此在多个参数使用时, 需要在注入的构造方法上加入 @Autowired 并且不可省略, 这样才能知道具体注入的是那个类对象
image.png
调用验证是否成功
image.png

三. @Resource 注解

上面的三种方法都是 Spring 官方提供的注入方式, 而在 JDK 中, 提供了另外的一种注入方式, @Resource 注解
同样的是将 ServiceStudent 类注入到 ControllerStudent 类中, 属性注入里不用 @Autowired 使用 @Resource 也是可以成功注入并调用的
image.png
那么, Setter 注入方法中, 使用 @Resource 注解可以吗 ?
image.png
通过验证可以发现, 同样是可以使用 @Resource 来搭配使用的, 那么为什么要造一个 @Resource 的轮子呢 ? 有什么用? 相比于 Spring 官方提供的三种注入方法又有什么优点呢 ?

@Resource 注解有什么用 ?

同样的, @Resource 注解通过上面的演示用例可以看到, 和其他注入方法一样, 都是将 Spring 容器中的类注入到其他类当中进行使用的

@Resource 和 @Autowired 有什么区别 ?

  • 相同点 : 功能上都可以进行类的注入
  • 不同点 :

1. @Autowired 注解比 @Resource 注解注入上更加强大**

@Autowired 可以支持构造方法注入和另外两种注入方式, 而 @Resource 却不支持构造方法注入, 只支持另外两种注入方式
image.png
当把 @Resource 注解添加到构造方法注入中时, 显示该注释方法不能用于构造方法之上, 具有一定局限性

2. @Autowired 注解来自于 Spring 官方提供的, 而 @Resource 注解来自于 JDK

3. @Resource 支持的参数比 @Autowired 更多

image.pngimage.png
可以看到的是, @Resource 提供的方法会参数更多, 在某些场景下会更加适用. 比如一个类多次注入到另一个类中, @Autowired 就无法实现, 而 @Resource 可以通过设置 name 属性的方式来解决这个问题.
@Autowired 注解只有一个参数可以设置, required(), 设置为 false 表示当前这个类在加载的时候不需要注入, 设置为 true 表示需要注入
image.png

4. @Autowired 和 @Resource 在 Spring 中查找的方式不同

比如同一个类已经注入多次的情况下, 通过属性注入的方式, 将该类注入到另一个类中, 例如下面, Student 这个类已经在 StudentCenter 类中被多次注入到 Spring 容器当中, 此时在将这个类通过属性注入到 StudentController2 这个类中.
image.png
image.png
获取 StudentController2 对象并调用
image.png
错误提示为找不到唯一的 Student 类型的 Bean 对象, 但是找到了两个可以匹配的 Bean 对象, 分别是 getStudentByName, getStudentByAge 这两个都是我们之前注入到 Spring 容器中的.

  • @Autowired 注解在获取 Bean 对象的时候, 先根据类型查找, 在根据名称查找

根据上面的问题, 结合 @Autowired 的查找方式来看 :
@Autowired 注解先根据类型去查找, 也就是 Student 类型去查找, 在加载依赖配置资源时, 发现了 Spring 容器中有两个 Student, 此时为了尽可能的去查找所需要的 Bean 对象, 会接着根据名称去查找, 也就是我们要注入的 student 这个名字去查找, 但是 Spring 容器中的 Student 类型的对象只有 getStudentByName, getStudentByAge 两个, 没有需要的名为 student 的, 因此报错找不到, 但是找到了两个 Student 类型的 Bean 对象

  • @Resource 注解在获取 Bean 对象的时候, 先根据名称查找, 在根据类型去查找

即使换成 @Resource 注解同样会报上面的错误, 找不到唯一的 Bean 对象, 这是因为 @Resource 注解先去根据名称查找, 但 Spring 容器中没有名称为 student 的 Bean 对象, 为了尽可能的去查找, 再去根据类型查找, 通过类型 Student 查找一看发现有两个该类型的 Bean 对象, 名称为 getStudentByName, getStudentByAge , 并非所需要的 Bean

那么上面的问题该怎么解决呢 ?

  1. 通过修改为指定名称

无论是 @Autowired 注解还是 @Resource 注解, 在通过查找时, 都只是查找到了指定类下有两个名称不一样的对象, 那么我们去指定获取这两个名称中的一个就可以获得了.
例如, 此处通过获取指定名称为 getStudentByName 的对象
image.png
很容易就拿到了这个对象, 那么, 如果我就是想用 student 这个名称怎么办呢 ? 有没有什么办法可以让我通过 student 这个名称也能获取到 ? 比如一个项目中, 这个变量已经使用了很多地方了, 去修改是很难的, 这时候就需要想办法去不替换如何实现了, 而 @Resource 就很好地解决了这个问题

  1. **使用 @Resource 注解并修改 name 属性 **

image.png
可以看到, 通过设置 @Resource 注解的 name 属性以后, 查找方式就为先根据名称查找, 而此时的名称就不在是 student 这个变量名称了, 而是我们设置的新 name 属性的名称 getStudentByName 这个名称了, 而这个名称在 Spring 容器中有且只有一份, 因此 很容易就找到了.

最后 ,无论是使用那种方法进行对象注入, 在使用的时候前提都是在 Spring 容器中存在这个对象 !

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值