一篇文章带你了解IoC
2020-03-02 本文已影响0人
JerrysCode
IoC是Spring的核心技术之一,中文名为控制反转,名字听起来比较抽象,什么是IoC?所谓“反转”到底反转了什么?为什么需要IoC?本篇文章将我的学习总结下来,供朋友们参考
什么是IoC
先介绍下概念术语
- IoC: Inversion of control, 中文翻译为控制反转。
- DI: Dependency Injection,中文翻译为依赖注入
- DL: Dependency Lookup,中文翻译为依赖查找
IoC的目的在于提供一种机制来管理对象的创新和销毁,以及对象之间的依赖关系。DI和DL是IoC的两种实现方式,三者的关系如下图所示
IoC.PNG
DI是IoC最常见的实现方式
通过容器把依赖的对象注入, 典型实现如Spring中的@Autowired
DL是IoC的另外一种实现
通过IoC容器提供的api查找依赖对象, 典型实现如Spring中的Context.getBean(MyBean.class)
为什么要使用IoC
应用程序通过多个类协同工作来实现业务逻辑,协作必然带来类之间的依赖。如何管理这种依赖关系呢?
如果不使用IoC,程序就需要自己实例化依赖的类。
举个例子:现实生活中每个汽车(Car)都有1个引擎(Engine),用下面的代码描述
public class Car {
private Engine engine;
public Car(){
this.engine = new Engine();
}
}
在这段代码中,Car依赖于Engine,Car类在构造方法中new了一个Engine实例。这种方法有几个缺点:
- 代码耦合:Car实例初始化必须依赖于Engine
- 无法适应变化:如果要更换一种新类型的Engine,必须修改Car代码
- 单元测试Car这个类,必须保证能够正确初始化Engine
使用IoC方式
为了解决上文提到的“耦合”问题,我们可以使用IoC来解耦。
public class Car {
private Engine engine;
public Car(Engine engine){
this.engine = engine;
}
}
这段代码中,Car不再通过new来获得Engine对象,而是 在运行时,由“第三方”程序创建Engine实例,并通过构造方法注入到Car类中,
这个“第三方”程序就是IoC容器。对Engine类进行实例化的"控制权"从Car类转移到了IoC容器中, 这区别于传统的new的方式,所以叫“反”转。
Spring如何实现IoC
通过前面的介绍,我们可以看到IoC是一种软件设计思想,并不是Spring特有的技术,Spring只是按自己的方式实现了这个思想。
Spring 的依赖注入由DI容器完成。
Spring提供xml和java annotation两种方式来声明Bean和描述Bean之间的关系
xml方式
- 通过xml声明Bean,指定Bean之间的依赖关系
<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!--声明Engine-->
<bean id="engine" class="com.jerry.Engine"/>
<!--声明Car,通过构造函数方式注入id为engine的Bean-->
<bean id="car" class="com.jerry.Car">
<constructor-arg ref="engine"/>
</bean>
</beans>
- 通过ApplicationContext获取Bean
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:my-spring-ctx.xml");
ctx.refresh();
Car car = ctx.getBean("car", Car.class);
car.start();
Java 配置方式
- 用一个配置类声明Bean,指定Bean之间的依赖关系
@Configuration //@Configuration 表明这是一个配置类
public class CarCofig {
@Bean
public Car car(){
return new Car(engine());
}
@Bean
public Engine engine() {
return new Engine();
}
}
- 通过ApplicationContext获取Bean
AnnotationConfigApplicationContext context = new
AnnotationConfigApplicationContext(CarCofig.class);
Car car = context.getBean(Car.class);
car.start();