1.Ioc容器的作用
在传统的编程方式中,对象之间的依赖关系往往通过直接调用其他对象的构造函数或方法来建立(即通过new对象的形式),这导致了代码之间的紧密耦合。这就如同一个齿轮组,它拥有多个独立的齿轮,这些齿轮相互啮合在一起,协同工作,共同完成某项任务。在这样的齿轮组中,如果有一个齿轮出了问题,就可能会影响到整个齿轮组的正常运转。增加了维护和扩展的难度。
为了解决对象之间的耦合度过高的问题,软件专家Michael Mattson提出了IOC理论,控制反转(IoC)是一种编程思想,它借助“第三方”容器来实现对具有依赖关系对象之间的解耦。这个“第三方”容器在Spring框架中就是IoC容器。IoC容器负责实例化对象,并管理对象之间的依赖关系,从而实现了依赖关系的反转:应用程序本身不再负责依赖对象的创建和维护,而是将这些任务交给了外部容器。
由于引进了中间位置的“第三方”,也就是IOC容器,使得A、B、C、D这4个对象分离开来,没有了耦合关系,齿轮之间的传动全部依靠“第三方”了,全部对象的控制权全部上缴给“第三方”IOC容器。
2.Ioc深入理解
接下来我们使用商家、外卖平台和点餐人之间的关系作为例子,形象地解释Spring IoC容器如何进行解耦的。
- 商家:提供食品或服务的实体,例如餐馆。
- 外卖平台:作为中间方,连接商家和点餐人,负责处理订单、支付等流程。
- 点餐人:使用外卖平台来点餐的消费者。
在没有外卖平台的情况下,商家和点餐人需要直接进行交互。商家需要了解哪些点餐人需要他们的服务,而点餐人则需要知道如何联系到商家,并手动处理订单、支付等流程。这种直接的耦合关系使得任何一方在改变时都可能影响到另一方,导致系统难以维护和扩展。
当引入外卖平台(即Spring IoC容器)后,上述问题得到了有效的解决。外卖平台作为中间层,解耦了商家和点餐人之间的直接交互。商家只需在平台上注册并提供服务,而点餐人则通过平台选择并点餐。这种解耦使得商家和点餐人可以独立地改变和扩展自己的业务,而不需要担心对另一方的影响。
在Spring IoC容器的语境中,商家和点餐人的类(即Bean)不再直接相互依赖,而是通过容器进行连接。容器负责管理Bean的创建、注入和生命周期,实现了它们之间的松耦合。
3.解耦过程详解
1. 商家入驻(Bean注册)
商家在外卖平台上注册自己的店铺和提供的菜品,这相当于在Spring IoC容器中注册Bean。商家不需要关心哪个点餐人会来点餐,只需要将自己的服务信息提供给外卖平台。
2. 点餐人浏览与选择(依赖声明)
点餐人打开外卖平台,浏览商家的菜品,并选择自己想要点的食物。这个过程在代码中,就相当于点餐人(服务使用者)的类中声明了对商家服务(Bean)的依赖。
3. 外卖平台处理订单(依赖注入)
当点餐人选择好菜品并提交订单时,外卖平台会负责处理这个订单。它会找到对应的商家,通知商家准备食物,并处理支付等事宜。这个过程在Spring IoC容器中,就是通过依赖注入来实现的。容器会检查点餐人的类的依赖声明,找到相应的商家Bean实例,并将其注入到点餐人的类中。
4. 商家制作食物(服务执行)
商家收到外卖平台的通知后,开始制作食物。在Spring IoC的语境中,这相当于商家Bean的实例执行了相应的方法,提供了服务。
5. 外卖平台配送食物(对象管理)
商家制作好食物后,外卖平台会负责配送。这包括安排骑手、跟踪订单状态等。在Spring IoC容器中,这相当于容器负责管理Bean的生命周期,包括它们的创建、销毁以及状态的维护。
6. 点餐人收到食物(服务使用)
最后,点餐人收到了外卖平台送来的食物,完成了整个点餐过程。在代码中,这就相当于点餐人的类使用了注入的商家服务,完成了相应的业务逻辑。
4.示例代码
- 编写餐品,点餐人,商家,外卖平台类。
- 商家类提供两个方法,分别是制作食物和获取顾客信息的方法。
- 外卖平台类提供下单的方法,在方法中调用商家的方法。
//餐品
public class Food {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//点餐人
public class Customer {
private String cusName;//顾客姓名
//注入顾客姓名
public void setCusName(String cusName) {
this.cusName = cusName;
}
public String getCusName() {
return cusName;
}
}
//商家-中餐馆
public class ZhongCanGuan {
//准备食物
public void doFood(Food food){
System.out.println("您有新的订单:请及时处理:顾客点了一份"+food.getName());
System.out.println("商家正在准备食物");
}
//获取顾客信息
public void getCustom(Customer customer){
System.out.println("顾客姓名:"+customer.getCusName());
}
}
//外卖平台
public class MeiTuan {
//商家入驻
private ZhongCanGuan zcg;
//顾客入驻
private Customer customer;
//set方式注入
public void setZcg(ZhongCanGuan zcg){
this.zcg = zcg;
}
public void setCustomer(Customer customer){
this.customer = customer;
}
//顾客通过外卖平台下单
public void orderFromRestaurant(Food food) {
//顾客点餐
zcg.doFood(food);
//餐馆获取顾客信息
zcg.getCustom(customer);
System.out.println("外卖平台已安排配送:" + food.getName());
}
}
- 编写applicationContext.xml配置文件,管理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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--定义餐品-->
<bean id="food" class="com.kdf.ioc.Food">
<!--set方式注入餐品名称-->
<property name="name" value="宫保鸡丁"/>
</bean>
<!--定义商家的bean 商家注册-->
<bean id="zcg" class="com.kdf.ioc.ZhongCanGuan"/>
<!--定义点餐人-->
<bean id="customer" class="com.kdf.ioc.Customer">
<!--注入顾客姓名-->
<property name="cusName" value="张三"/>
</bean>
<!--定义外卖平台-->
<bean id="meiTuan" class="com.kdf.ioc.MeiTuan">
<!-- 注入商家Bean -->
<property name="zcg" ref="zcg" />
<!-- 注入点餐人Bean -->
<property name="customer" ref="customer" />
</bean>
</beans>
- 编写测试类,模拟点餐
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class DianCanTest {
public static void main(String[] args) {
// 创建Spring IoC容器实例
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 从容器中获取外卖平台Bean
MeiTuan mt = (MeiTuan) context.getBean("meiTuan");
//从容器中选择餐品
Food food = (Food) context.getBean("food");
//通过外卖平台下单
mt.orderFromRestaurant(food);
}
}
- 运行结果如下: