[Java提升]Spring IoC概念理解、Spring对Bean的管理方式和几种注入方法的分析

Spring IoC概念理解、Spring对Bean的管理方式和几种注入方法的分析

一、核心概念

  • IOC: 控制反转( Inversion of Control)/依赖注入(Dependency Injection) :
    由Spring容器负责对象的生命周期和对象之间的依赖关系
    如何理解控制反转?
  • 谁控制谁? IOC容器控制对象。
    传统的开发模式,我们都是采用直接new对象的方式来创建对象,每个依赖的对象都由自己进行控制。但是有了IOC容器后,则直接由IOC容器来进行控制。IOC容器可以类似的理解为房产的中介,管理着许多房产的资料,如果想要租房或者买房,直接从中介那里获取信息,将原来的主动寻找信息转变为了从指定的地方获取。原来是需要什么东西自己去拿,现在是需要什么东西让别人(IOC Service Provider)送过来
  • 谁被控制?
    对象被控制。被IOC容器控制
  • 为什么叫反转?
    正: 自己创建对象,自己进行对象管理
    反转: 所依赖的对象直接由IoC容器创建后注入到被注入的对象中,所依赖的对象的获取方式发生了反转
  • 什么被反转了?
    所依赖的对象的获取方式被反转了

二、Spring加载Bean的过程

Spring 容器直接管理的对象称之为 bean,其加载过程分为读取定义、根据定义加载两部分:

  • 获得 bean 的定义: BeanFactory 使用 BeanDefinitionReader 加载 BeanDefinition 到 BeanDefinitionRegistry 进行注册。

  • 加载 bean: 调用 BeanFacotry 的 getBean 方法时,根据 BeanDefinition 来加载对应的 Bean。
    在加载 bean 的过程中,AbastractBeanFactory 作为 BeanFactory 接口的抽象实现,将 BeanFactory 的 getBean 操作委托到了内部的 doGetBean 方法。doGetBean 内部进行各种逻辑判断(是否注册过、是否已经初始化过、依赖的 bean 是否已经加载等)后,调用 creatBean 进行 bean 的创建。creatBean 是一个抽象方法,这里来看一下 AbstractAutowireCapableBeanFactory 中的具体实现。 AbstractAutowireCapableBeanFactory 的 createBean 方法在真正创建前,会先调用 resolveBeforeInstantiation 来处理需要 AOP 增强的 bean,如果该方法返回了代理后的 bean,则直接返回该 bean。

三、注入方式

IoC的注入方式总共有如下几种:

  1. 构造器注入
@Controller
pulic class YoungMan{
	@Autowired
	private final BeautifulGirl beautifulGril;

	YoungMan(BeautifulGirl beautifulGirl){
    	    this.beautifulGirl = beautifulGirl;
	}
}
  1. setter方式注入
@Controller
public class YoungMan{
	@Autowired
	private BeautifulDirl beautiofulGirl;
	
	public void setBeautifulGirl(BeautifulGirl beautifulGirl){
		this.beautifulGirl = beautifulGirl;
	}
}
  1. field注入
@Controller
public class YoungMan {
  @Autowired
  private BeautifulGirl beautifGirl;
  
}

那么为什么会有三种注入方式呢?

  • 其中field注入的方式是最简介的一种注入方式,也非常符合人的第一逻辑,只要加上@Autowired注解放在实例域上依赖注入就完成了,也不需要去写setter和Constructor。但是使用filed注入的方式存在着几个问题:
    • 一是无法复用该实现类。
      没有Setter 或有参构造器意味着通过 Field Injection 注入的实例域无法通过常规的手段进行初始化,必须要依赖DI容器。这就意味着这不是一个纯粹的 POJO 了,这个类彻底放弃了对自身依赖的管理。使用者也往往无法清晰的知道正确使用这个类所需要的依赖,而 Setter 或 Construtor 可清晰的告知使用者这个类的依赖。
      Field Injection 也让我们感觉到注入依赖实在是太容易了,声明下变量,一个注解就搞定了,于是经常导致注入过多的依赖。当一个类依赖了过多其他的类,往往意味着违反了单一职责原则 (Single Responsibility Principle, SRP)。这个类的很多责任应该划分到其他类中,而不是在 DI 的便利下增加其责任。

    • 二是会存在循环注入的情况,A->B,B->A。

public class A {
    @Autowired
    private B b;
}
 
public class B {
    @Autowired
    private A a;
}

setter注入是在spring3.0刚推出的时候官方推荐的注入方式,当时的原话是:

The Spring team generally advocates setter injection, because large numbers of constructor arguments can get unwieldy, especially when properties are optional. Setter methods also make objects of that class amenable to reconfiguration or re-injection later. Management through JMX MBeans is a compelling use case.
Some purists favor constructor-based injection. Supplying all object dependencies means that the object is always returned to client (calling) code in a totally initialized state. The disadvantage is that the object becomes less amenable to reconfiguration and re-injection.

翻译过来就是:Spring团队提倡使用setter注入的方式,因为大量的构造器声明会显得臃肿,特别是当属性是可选的时候。setter方法该类的对象在以后能够重新配置或者重新注入。
一些人追求构造器注入,他们认为所有的对象依赖意味着这个对象始终以完全初始化的状态返回到调用他的代码。这种方式的缺点是对象不适合重新配置和注入。

其存在的原因不外乎认为setter比较灵活

但是在Spring4.x之后,官方又开始推荐构造器注入的方式。官方是这么说的:

The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state.
Spring团队大力提倡(花式秀,之前还提倡setter方式呢)构造器注入的方式,因为啊,这个构造器注入的方式能够确保注入的组件不可变,并确保所需的依赖不是null。而且啊,构造器注入的依赖总是能够在返回客户端(组件)代码的时候保证完全初始化的状态

构造器注入的方式保证了两点:

  • 依赖不可变:上例中的final关键字
  • 依赖不为空:当要初始化实例化YoungMan的时候,由于YoungMan实现了有参数的构造函数,故不会调用默认的无参构造器,此时需要由Spring容器传入所需要的BeautifulGirl,保证所需要依赖的对象不为空。也保证了完全初始化

四、总结

我们在使用IOC的时候,首先考虑使用构造器的方式进行注入,但当一个依赖有多个其他的类,使用field注入或者setter注入方式来指定注入的类型或许会方便些。(但是,有多个依赖不一定是好事,此时要注意SRP原则Single Responsibility Principle,过多的职责需要划分到其他的类当中去,而不是在DI的便利下增加其责任。)
Spring 官方目前推荐尽量使用构造器注入。

  1. 依赖不可变:因为构造注入可以注入 final域,让依赖更加的不可变。
  2. 依赖不为空:能避免依赖为null的情况。而且如果注入了过多的依赖,构造器也会显得臃肿不堪,会提示开发者注意 SRP 原则。另外,Spring 4.3 版本之后,构造器注入的情况下可以省去 @Autowired 注解,代码可以变得更加纯粹,与框架依赖更少。
  3. 所需依赖被完全初始化
  4. 不存在拟循环依赖
    当然,Setter 注入方式也有它的优点,在许多情况下还是需要通过 setter 来进行注入的。比如注入一些可选的依赖,或者需要在运行时动态改变的依赖。

猜你喜欢

转载自blog.csdn.net/qq_31749835/article/details/88764527