本文内容基于《Spring 5企业级开发实战》,周冠亚、黄文毅著。
1. IoC的概念
IoC是Inversion of Control的简写,即控制反转。IoC并不是一门技术,而是一种设计思想。
在没有IoC设计的场景下,开发人员在使用所需的对象时,需手动创建各种对象,如new Student()。
有了IoC这样的设计思想,在开发中,意味着将设计好的对象交给容器管理,而不再是像传统的编程方式中,在对象内部直接控制对象。
在传统Java程序设计中,直接在某个对象内部new创建另一个对象,这是开发人员主动去创建依赖对象;IoC的设计思想是通过专门的对象容器来创建和维护对象;即对象由开发人员控制 -> 对象由容器来控制。
1.1 依赖倒置原则
抽象不应该依赖于具体实现,具体实现应该依赖于抽象。
【实例】
一个人,需要通过某种交通工具去上班。
- 不遵循依赖倒置原则的设计风格:
public class Person {
/*private class Bike {
public void go() {
System.out.println("bike go");
}
}*/
private class Car {
public void go() {
System.out.println("car go");
}
}
/**
* 自行车
*/
//private Bike bike;
/**
* 小车
*/
private Car car;
/**
* 创建人的时候为他创建自行车/小车
*/
public Person() {
//this.bike = new Bike();
this.car = new Car();
}
/**
* 上班
*/
public void goWork() {
//bike.go();
car.go();
}
public static void main(String[] args) {
Person person1 = new Person();
person1.goWork();
}
}
上述代码表达了这个人如果从骑自行车上班变为开车上班,需要对代码进行重构,意味着每一次更改都需要重构。
- 遵循依赖倒置原则的设计风格:
public class Person {
private interface Movable {
void go();
}
private class Bike implements Movable {
@Override
public void go() {
System.out.println("bike go");
}
}
private class Car implements Movable {
@Override
public void go() {
System.out.println("car go");
}
}
/**
* 交通工具
*/
private Movable movable;
/**
* 创建人的时候为他创建交通工具
*/
public Person() {
//this.movable = new Bike();
this.movable = new Car();
}
/**
* 上班
*/
public void goWork() {
movable.go();
}
public static void main(String[] args) {
Person person1 = new Person();
person1.goWork();
}
}
以上代码表达了,将自行车、小车抽象成交通工具,如果要换交通工具只需要修改一句代码就行了。
1.2 依赖注入
上述代码遵循了依赖倒置,可以发现其中的Bike或是Car还是需要开发人员主动去创建,这时候就可以应用IoC的思想,让依赖关系由容器来管理。
依赖注入(DI,Dependency Injection)是Spring实现IoC的容器的一种重要手段:
@Component
public class Person {
private interface Movable {
void go();
}
@Component("bike")
private class Bike implements Movable {
@Override
public void go() {
System.out.println("bike go");
}
}
@Service("car")
private class Car implements Movable {
@Override
public void go() {
System.out.println("car go");
}
}
/**
* 交通工具
*/
//@Resource(name = "bike")
@Resource(name = "car")
private Movable movable;
/**
* 上班
*/
public void goWork() {
movable.go();
}
}
因为需要IoC容器进行依赖注入,所以这里就不能用main方法来测试了,必须在Spring上下文中,创建单元测试类:
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonTest {
@Autowired
Person person;
@Test
public void goWork() {
person.goWork();
}
}
【注】IoC容器中存放的是单例模式的对象。
【理一下控制反转、依赖倒置、依赖注入的关系】控制反转是一种软件设计模式,其遵循了软件工程中的依赖倒置原则,而依赖注入是Spring实现控制反转的一种方式。
2. Spring IoC的实现方式
2.1 XML方式
public interface Movable {
void go();
}
public class Bike implements Movable {
public Bike() {
}
@Override
public void go() {
System.out.println("bike go");
}
}
public class Car implements Movable {
private String name;
public Car(String name) {
this.name = name;
}
@Override
public void go() {
System.out.println(name + " car go");
}
}
public class Person {
/**
* 交通工具
*/
//@Resource(name = "bike")
@Resource(name = "car")
private Movable movable;
/**
* 上班
*/
public void goWork() {
movable.go();
}
}
<!--?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.xsd">
<!--无参构造-->
<bean id="bike" class="com.yeta.review.c.Bike" />
<!--有参构造-->
<bean id="car" class="com.yeta.review.c.Car">
<constructor-arg index="0" value="BMW" />
</bean>
<bean id="person" class="com.yeta.review.c.Person" />
</beans>
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration("classpath:spring-ioc.xml")
public class PersonTest {
@Autowired
Person person;
@Test
public void goWork() {
person.goWork();
}
}
2.2 注解方式
注解方式与1.2中代码一致,这里区分一下几个注解:
- @Component:将Java类标记成一个Spring Bean组件;
- @Service:将业务层实现类标记成一个Spring Bean组件;
- @Controller:将控制层类标记成一个Spring Bean组件;
- @Repository:将持久层实现类标记成一个Spring Bean组件。