什么是 Spring IOC 容器?
控制反转即 IoC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的 “控制反转” 概念就是对对象组件控制权的转移,从程序代码本身转移到了外部容器。
Spring IOC 负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期。)
控制反转 (IoC)
作用
● 管理对象的创建和依赖关系的维护。对象的创建并不是一件简单的事,在对象关系比较复杂时,如果依赖关系需要程序猿来维护的话,那是相当头疼的
● 解耦,由容器去维护具体的对象
● 托管了类的整个生命周期,比如我们需要在类的产生过程中做一些处理,最直接的例子就是代理,如果有容器程序可以把这部分处理交给容器,应用程序则无需去关心类是如何完成代理的
在实际项目中一个 Service 类可能依赖了很多其他的类,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。
在 Spring 中, IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象。
Spring IoC 的实现机制
Spring 中的 IoC 的实现原理就是工厂模式加反射机制。
示例:
interface Fruit {
public abstract void eat();
}
class Apple implements Fruit {
public void eat(){
System.out.println("Apple");
}
}
class Orange implements Fruit {
public void eat(){
System.out.println("Orange");
}
}
class Factory {
public static Fruit getInstance(String ClassName) {
Fruit f=null;
try {
f=(Fruit)Class.forName(ClassName).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return f;
}
}
class Client {
public static void main(String[] a) {
Fruit f=Factory.getInstance("com.jourwon.spring.Apple");
if(f!=null){
f.eat();
}
}
}
依赖注入 (Dependency Injection)
控制反转 IoC 是一个很大的概念,可以有不同的实现方式。其主要实现方式有两种:依赖注入和依赖查找
依赖注入:相对于 IoC 而言,依赖注入 (DI) 更加准确地描述了 IoC 的设计理念。所谓依赖注入(Dependency Injection),即组件之间的依赖关系由容器在应用系统运行期来决定,也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。组件不做定位查询,只提供普通的 Java 方法,让容器去决定依赖关系。
依赖查找(Dependency Lookup):容器提供回调接口和上下文环境给组件。EJB 和 Apache Avalon 都使用这种方式。
依赖查找也有两种类型:依赖拖拽(DP)和上下文依赖查找 (CDL)。
有哪些不同类型的依赖注入实现方式?
依赖注入是时下最流行的 IoC 实现方式,依赖注入分为接口注入(Interface Injection),Setter 方法注入(Setter Injection)和构造器注入(Constructor Injection)三种方式。其中接口注入由于在灵活性和易用性比较差,现在从 Spring4 开始已被废弃。
构造器注入:构造器注入是容器通过调用一个类的构造器来实现的,该构造器有一系列参数,每个参数都必须注入。
Setter 方法注入:Setter 方法注入是容器通过调用无参构造器或无参 static 工厂方法实例化 bean 之后,调用该 bean 的 setter 方法来实现的依赖注入。
构造器注入和 Setter 方法注入的区别
最好的解决方案是用构造器参数实现强制依赖,setter 方法实现可选依赖。
IoC容器的初始化过程
IoC容器的初始化过程由refresh()方法来启动,这个方法标志着IoC容器正式启动,过程如下:
- BeanDefinition的Resource定位。其指的是BeanDefinition的资源定位,它由ResourceLoader通过统一的Resource接口完成
- BeanDifinition的载入和解析。把用户定义好的Bean表示成IoC容器的内部数据结构,即BeanDefinition, 具体的载入过程由BeanDefinitionReader完成,解析过程由BeanDefinitionParserDelegate完成。
- 向IoC容器注册BeanDefinition。通过调用BeanDifinitionRegistry接口的实现完成。此过程把载入过程中解析得到的BeanDefinition向容器中进行注册。实际上是把BeanDefinition注册到一个HashMap中,IoC容器通过这个HashMap来持有这些BeanDefinition的数据。
IoC容器初始化过程中,一般不包括Bean依赖注入的实现。在SpringIoC的设计中,Bean定义的载入和依赖注入是两个独立的过程。依赖注入一般发生在应用第一次通过getBean向容器中索取Bean的时候。
IoC容器的依赖注入
IoC容器初始化的过程完成的主要功能是在IoC容器中建立BeanDefinition的数据映射。
- 对Bean的依赖关系进行注入。依赖注入的过程是用户第一次向IoC容器索要bean时触发的(当然也有例外,可以通过在BeanDefinition信息中控制lazy-init属性来让容器完成的Bean的预实例化),当用户向IoC容器索要Bean时,在BeanFactory中的getBean()接口定义,这是触发依赖注入发生的地方。
- getBean是依赖注入的起点,之后会调用createBean。在这个过程中,Bean对象会依据BeanDefinition定义的要求生成,createBean不但生成了需要的Bean,还对Bean的初始化进行了处理,如实现了在BeanDefinition中的init-method属性定义,Bean后置处理器等。
- Bean对象生成之后,设置各个对象之间的依赖关系,通过使用BeanDefinitionResolver来对BeanDefinition进行解析,然后注入到property中。
Strategy类是Spring用来生成Bean对象的默认类,它提供了两种实例化Java对象的方法,一种是通过BeanUtils,它使用了JVM的反射功能,一种是通过CGLIB(字节码生成器的类库 )来实现。