一、什么是IOC
IOC全名叫做控制反转。英文名是Inversion of control。有些时候也叫作依赖注入,其实这是俩不同的概念。有些时候把他们划等号是因为描述的是一个功能,从两个角度来分析。
既然名字叫做IOC我们来看看,控制什么,反转什么。
1、控制什么:控制对象的创建和销毁,指的是控制对象的生命周期。
2、反转什么:之前我们创建一个对象都是new,现在有了IOC了,指的是把对象的控制权交给了IOC容器。
这俩概念如果你还不理解,可以看下面一个简单的案例,来体会一下这个控制反转的思想。
假如说有一个用户,有一辆车,每天就干两件事,那就是上学和回家。
阶段一:用户自己创建一辆车完成功能
首先定义一辆车:
public class Benz{ @Override public void start() { System.out.println("奔驰车启动了"); } @Override public void turn() { System.out.println("奔驰车转弯了"); } @Override public void stop() { System.out.println("奔驰车刹车了"); } }
然后用户是这样来实现:
public class User { private Benz car = new Benz(); public void goSchool(){ car.start(); car.turn(); System.out.println("到达学校"); car.stop(); } public void goHome(){ car.start(); car.turn(); System.out.println("到家了"); car.stop(); } }
现在使用是用户自己创建一辆车完成上班下班的功能,肯定是不合理。因为用户只是想用一辆车来完成上班下班的功能,每次回家之前先造一辆车太麻烦了。怎么办呢?干脆用户需要车的时候,给他传过去一辆。
阶段二:给用户传递一辆奔驰车来完成功能
如果给用户传递呢?方法很简单,只需要在用户的构造方法上,加一个车的参数即可。
public class User { private Benz car; public User(Benz car) { this.car = car; } public void goSchool(){ car.start(); car.turn(); System.out.println("到达学校"); car.stop(); } public void goHome(){ car.start(); car.turn(); System.out.println("到家了"); car.stop(); } }
现在貌似实现了我们的功能,用户需要一辆车的时候,只需要给他传递过去一辆车即可,没毛病吧,但是果断时间需求又变了,用户又买了一辆宝马,不想用奔驰了。于是我们手忙脚乱的改,把所有的奔驰类全换成宝马类,有过一段时间用户又买了一辆奥迪,又开始手忙脚乱的改,这也太麻烦了。干脆把所有的车换成一个接口。
阶段三:给用户传递一辆车完成功能(不是具体的车)
于是,我们先定义一个car接口,然后让奔驰宝马实现这个接口就好了。
public interface Car { public void start(); public void turn(); public void stop(); } public class Benz implements Car{ @Override public void start() { System.out.println("奔驰车启动了"); } @Override public void turn() { System.out.println("奔驰车转弯了"); } @Override public void stop() { System.out.println("奔驰车刹车了"); } } //宝马、奥迪一样
然后我们的用户类里面只需要给他传递一辆车,不指定是什么车。
public class User { private Car car; public User(Car car) { this.car = car; } public void goSchool(){ car.start(); car.turn(); System.out.println("到达学校"); car.stop(); } public void goHome(){ car.start(); car.turn(); System.out.println("到家了"); car.stop(); } }
现在完事具备,好像已经很完美了,用户上学回家的过程,使用什么车,给他传递一辆就OK了,但是我们想过没有,谁给用户传递的,难道我们在测试的时候,自己写代码先new一辆嘛?用户第二天还想用宝马,难道我们再new一辆嘛?而且如果用户每天换一辆,而且有十几辆车,怎么办呢?假如又有一个新用户,也想实现上面的功能,这时候就需要想办法了。
干脆呀,把这些车全部放在一个容器内,需要的时候拿出来就好了,就不用我们自己创建一个给用户了。这样不管是谁想要车,什么时候想要车,需要的时候去这个容器内去拿就好了。这时候就是一个控制反转的思想,我们把车的创建销毁交给容器,而且控制权给容器。对我们来说,我们只是使用的功能。
现在我们使用一下SpringIOC,通过案例进一步了解其思想。
二、代码示例
第一步:添加依赖
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.2.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.2.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
在这里我使用的是5.2的版本。
第二步:在main文件夹下的resources目录下,新建Spring.xml文件
<?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="bean1" class="com.fdd.bean.Benz"/> <bean id="bean2" class="com.fdd.bean.BMW"/> </beans>
注意在这里,我们注入进来了两个bean,一个是奔驰类,一个是宝马类,并分别给他们一个id。只需要这两行代码就相当于把两辆车放到了IOC容器。
第三步:新建一个MyTest测试类
public class MyTest { @Test public void test1(){ //首先通过上下文环境和IOC容器链接起来 ApplicationContext context = new ClassPathXmlApplicationContext("Spring.xml"); //然后在容器内拿去我们想要的bean Benz benz = context.getBean("bean1", Benz.class); //最后使用我们的bean User user = new User(benz); user.goHome(); user.goSchool(); } }
案例很简单,首先和容器链接起来,然后拿到我们的bean,最后再去使用,现在你会发现,不管是哪一个用户在什么时候用哪一个bean,都可以从我们的容器内去获取。是不是很简单。
这里只是给出了一个基本的案例,其实关于IOC容器如何管理我们的bean还有很多内容。比如说上面只是简单的注入了一个最普通的bean,如果是由工厂创建的怎么注入,bean里面包含了其他的bean如何注入等等。篇幅有限,今后推出剩余系列的文章。