一. 获取 Bean 对象
获取 Bean 对象就是把某个对象从 Spring 容器中取出来放到某个类中, 也叫做对象装配或者对象注入, 对于获取的前提都是对象存在于该容器中(对象在指定扫描包路径底下, 或者配置文件单独添加了 bean 对象注入)
二. 对象注入的三种方法
1. 属性注入
属性注入使用 @Autowired 注解, 并且不需要 new 对象
例如, 将 ServiceStudent 类注入到 ControllerStudent 类中
可以看到, 将其注入到 ControllerStudent 中时, 是不需要进行 new 对象的
获取 Bean 对象并使用, 来验证是否真的通过 @Autowried 注解将属性注入到了其中
运行结果可以看到, 成功的调用了 ServiceStudent 中的 fun( ) 方法, 成功的将 ServiceStudent 类通过 @Autowired 注解成功注入到 ControllerStudent 类中
属性注入的问题
- 属性注入非常的方便, 但是为什么我们在 main 方法中不直接使用 @Autowired 将 ControllerStudent 直接获取到在使用, 岂不是方便很多 ?
尝试后发现, 报错提示他不是一个静态的对象, 哪加上 static 让它变成静态对象是不是可以用了呢 ?
加了静态方法以后, 还是不行 这是为什么呢 ?
这和 JVM 的加载有关系, 静态方法的加载是在 Spring 之前的. 因此, 在对象还未被注入到 Spring 容器当中时, ControllerStudent 对象就已经被加载了, 此时从容器当中拿不到这个对象, 再去使用就会空指针异常了.
- 无法注入被 final 修饰的对象
可以看到的是, 当使用 @Autowired 注解去注入对象的时候, 报错提示 serviceStudent 变量无法被初始化, 原因很简单, final 的对象在创建时, 都需要进行初始化, 而此处使用 @Autowired 注解注入时, 并没有立刻给 serviceStudent 去进行初始化, 而是在执行时从容器中去获取后才赋值给它.
- 有违反单一性的风险
从上面可以将 ServiceStudent 类注入到 ControllerStudent 中就可以看出来, @Autowired 属性注入是一种非常简单的获取 Bean 对象的方法, 因此在使用中, 极有可能注入多次, 是否符合项目的单一性就变成了一个问题.
- 兼容性差
只能在 IoC 容器中使用.
属性注入的优点
从上面的注入示例看到, 属性注入用起来非常的爽, 就主打一个简单, 因此优点也是十分的明显, 就是使用简单 !
2. Setter 注入
顾名思义, 简单的理解就是 通过 set 方法将类注入对象, 需要使用 @Autowired 注解搭配
同样的, 将 ServiceStudent 类注入到 ControllerStudent 中** **
获取 Bean 对象并调用验证是否真的将 ServiceStudent 注入到 ControllerStudent 中
通过结果来看, 是成功将 ServiceStudent 类注入到了 ControllerStudent 中的
Setter 注入的问题
- setter 注入体现在哪里 ?
对于 Setter 注入, 不是说顾名思义就是通过 set 方法注入嘛 ? 体现在哪里了呢 ?
通过刚刚的使用可以看出, 在创建所需要的注入类对象后, 通过生成 set 方法, 将要注入的对象传入其中, 即可完成赋值
- @Autowired 在这里起什么作用 ?
为什么 setter 注入要用到 @Autowired 呢 ? ** **set 方法中的参数又是谁给赋值的呢 ?
对于这些疑问, 通过 @Autowired 从 Spring 中去获取到对象, 并将这个对象传入给 set 方法的参数中, 最后通过参数的形式赋值给所需要的类对象进行注入. 如果没有 @Autowired 将无法从 Spring 容器中获取对象后注入
- 无法注入 final 修饰的变量
和属性注入一样, 都无法将 final 修饰的变量注入其中, 原因和属性注入中是一样的
- 注入的对象可以修改
由于 setter 注入是一个简单的 set 方法注入, 因此在注入的类中, 可以去任意调用以及修改, 可能会存在被注入多次不同的对象
在当前要注入的 ControllerStudent 中, 可以去任意的修改注入的 serviceStudent 对象
Setter 注入的优点
- 非常符合单一性
由于 setter 注入是使用普通的 set 方法进行注入, 而 set 方法每次只能传一个参数, 及时要注入两个对象, 也是设置两个 set 方法, 因此它非常的符合单一性, 不会存在多份的可能性
3. 构造方法注入
构造方法注入和 Setter 注入非常的像, 只不过 setter 注入是需要体统 set 方法, 而构造方法注入提供的是构造方法. 为什么说像呢 ? **因为他们都需要搭配 @Autowired 使用 **同样的, 将 ServiceStudent 类注入到 ControllerStudent 类中
获取 Bean 对象并调用方法进行验证
通过调用方法可以看出, 使用构造方法搭配 @Autowried 注解成功的将对象注入成功
构造方法注入的问题
- @Autowired 在这里起什么作用 ?
为什么 构造方法注入要用到 @Autowired 呢 ? ** **构造方法中的参数又是谁给赋值的呢 ?
对于这些疑问, 通过 @Autowired 从 Spring 中去获取到对象, 并将这个对象传入给构造方法的参数中, 最后通过参数的形式赋值给所需要的类对象进行注入. 如果没有 @Autowired 将无法从 Spring 容器中获取对象后注入
- 多个参数构造时, 略显臃肿
既然是构造方法注入, 那么肯定是可以同时提供多个参数的, 意味着可以同时注入多个参数, 那么会显得程序有点臃肿, 同时还应当去考虑当前类是否符合程序的单一职责的设计模式, 只实现一个功能.
构造方法的优点
- 通用性
带参数得构造方法,在很多的框架中都可以用, 通用性也会更好.
- 保证注入对象被完全初始化
因为类加载的关系, 一个类在进行加载的时候, 先进行的是实例化, 在进行初始化, 在进行初始化的时候去执行构造方法, 在执行构造方法的时候就会把依赖的对象注入到当前类当中, 在底下执行使用的时候, 该对象一定是被完全初始化了的.
- 注入对象不可以被修改
构造方法只能执行一次, 因此注入的对象是不可以被修改的
- 可以一次注入多个类
可以同时注入多个类, 虽然会让程序略显臃肿, 但在某些必要场景下, 一次传入多个会更简单
获取 Bean 对象并调用验证
可以看到, 构造方法可以同时注入多个类
- 可以注入 final 修饰的变量
此处 idea 提示我们, 该字段可能是 final 修饰的, 当加入 final 修饰以后, 就没有提示了
相比于前面两种注入方法, 构造方法支持注入 final 修饰的变量, 对于一些需要的情况下会更适用
**那么, 为什么构造方法就可以注入 final 修饰的对象呢 ? **
在这, 就需要去理解 final 的用法了, 对于 final 修饰的对象需要满足其中的条件之一
- final 在使用的时候, 直接进行赋值
- final 必须在构造方法中赋值
由于在构造方法中, final 修饰的变量已经在构造方法中进行赋值了, 因此构造方法注入可以注入 final 修饰的变量
- **当前类中如果只有一个构造方法, 允许不使用 @Autowired **
同样调用运行观察
可以看到的是, 当我们构造方法中只有一个构造方法时, 是可以不添加 @Autowired 注解的, 这是 Spring 官方对于该注入方法进行的单独处理.
如果有多个构造方法的时候, 不添加 @Autowired
这里为什么会报错呢 ? 报错的内容是 serviceStudent 和 componentStudent 变量无法进行初始化. 为什么在添加了多个构造方法, 同时也添加了 @Autowired 注解还是会报错 ?
原因很简单, 由于这两个字段都是 final 修饰的, 需要满足两个条件中的一个, 此处为在构造方法时就需要赋值, 因此只能使用两个参数的构造方法注入, 又因为提供了其他两个的构造方法, 当去使用这两个构造方法的时候, final 修饰的字符已经被初始化了, 无法在进行初始化修改了是一个不可变的字段了.因此在使用当中, 多个参数的构造方法注入时, 应当考虑好是否使用 final 修饰, 此处将 @Autowired 注释放在了第二个构造方法中, 注入的就是指定的第二个构造方法的参数, 因此在多个参数使用时, 需要在注入的构造方法上加入 @Autowired 并且不可省略, 这样才能知道具体注入的是那个类对象
调用验证是否成功