spring 基础二
前言
Ioc控制管理这所有实例,在开发中我们只需要在合适的时机拿到我们需要的实例进行使用,我们怎么能够简单的获取到这些加载的实例?
依赖注入(DI)
通过查看官网依赖注入的方式有两种:
- 构造方法注入
- setter方法注入
通过代码来验证这两种注入方式:
通过配置xml 来指定注解方式,分别可以通过构造与setter方法创建FirstService实体,但是两种方式同时使用,实体FirstService的创建与注入过过程是怎样的?
可以看到结果,先是通过构造方法创建注入了FirstService实体,然后又通过setter的方式注册一遍,意味着:setter方式注入的实体会覆盖通过构造方法注入的实体,在开发会不会出现一些奇怪的现象引起不容易排查的错误?其实Spring在官网上有说到这一点:
官网的意思是:
- 构造方法注入与setter方法注入可以混合使用,对于一些强制的依赖最好使用构造方法注入,对于一些可以选择的依赖我们通过setter方式注入,并且可以在setter方法上通过@Required注解表示这个依赖是强依赖.
- Spring团队通常提倡构造函数注入,因为它可以让你将应用程序组件实现为不可变对象,并确保所需的依赖项不为null.
- 使用没有源代码的第三方依赖时,第三方依赖又没有提供setter方式注入,此时只能通过构造方式注入.
所以说是用哪种注入方式并没有强制规定,通常情况下都是根据具体情况来使用依赖注入的方式,但是可以遵循一个原则:强依赖使用构造注入,可配置使用setter方式注入.
方法注入
我们知道知道怎样注入我们需要使用的实体,通过注入我们使用实体的方法进行一些业务处理,但是往往注入过后处理得到的结果不是我们所预期的,比如:
我想通过FirstService中的add方法来计算值,预期三次的输入结果是 1,2,3
但是实际结果却是 1 , 3 , 6 实际结果说明了什么?我们拿到的FirstService对象是一个单例,因为在依赖注入阶段我们就完成了FirstService的注入,之后我们在调用测试方法时,不会再去进行注入,所以我们一直使用的是同一个对象。那就要每次调用方法时拿到新的对象该怎么办?
查看Spring文档:
针对这种情况Spring提供了解决办法,查看官网说明:
第一段说明问题:
在大多数应用场景中,容器中的大多数bean是单例的。 当单例Bean需要与另一个单例Bean协作或非单例Bean需要与另一个非单例Bean协作时,通常可以通过将一个Bean定义为另一个Bean的属性来处理依赖性。 当bean的生命周期不同时会出现问题。 假设单例bean A需要使用非单例(原型)bean B,也许在A的每个方法调用上都使用。容器仅创建一次单例bean A,因此只有一次机会来设置属性。 每次需要一个容器时,容器都无法为bean A提供一个新的bean B实例。
第二段提供解决办法:一种解决方案是放弃某些控制反转。 可以通过实现ApplicationContextAware接口,并通过对容器进行getBean("B")调用来使bean A知道该容器,以便每次bean A需要它时都请求一个(通常是新的)bean B实例。
官网直接告诉我们,别用什么控制反转了,这种情况你每次使用就重新创建一个实体,可以通过实现ApplicationContextAware接口,每次使用的时候通过getBean来拿一个新的实体,完美解决.
那方法注入提供的两种方式有啥用?
- Lookup Method Injection
Spring框架通过使用从CGLIB库生成字节码来动态生成覆盖该方法的子类来实现此方法注入。
但是官网上也给出了很多规定:
注解的格式也是有要求:
- Arbitrary Method Replacement
通过xml配置完全替换方法的执行逻辑,这样实现注入的实体为新的实体.
方法注入让获取到新的实体更加便捷(注解或者xml配置),并且更加灵活,使代码的耦合性更低,但是方法注入依赖于动态代理,所以限制又变得很多.
小结
回顾一下,Spring官网是如何解释依赖注入的?
Spring 注入的是一个对象自身的依赖,一个对象自身的依赖可以理解为该对象所拥有的属性,但是一个完整的对象是由 属性 + 行为(方法)所构成,在Spring中通过属性注入与方法注入来完全掌控一个实体,属性注入分为:构造函数注入与setter方法注入,方法注入分为LookUp Method和Replace Method,方法注入都依赖与动态代理,方法注入是对属性注入的一种补充,因为当只有属性注入时,实体对象的行为(所拥有的方法)会变得无意义.