一、IOC/DI概述
IOC(Inversion of Control)(控制反转)
DI(Dependency injection)依赖注入
依赖注入和控制反转其实就是一个事物的两种不同的说法而已,本质上是一回事。从技术层面来讲,依赖注入是控制反转的一种特殊实现,其基本思想就是把类的依赖从类的内部转到外部,以减少依赖,而在java中,类与类之间外部的注入方式只有两种,一个是构造器注入,一个是setter注入
在软件开发过程,系统的各个对象之间,各个模块之间,软件系统之间与硬件系统之间,或多或少都会存在耦合关系,如果一个系统的耦合程度过高,就会造成难以维护的问题,但是完全没有耦合度的代码是不可能工作的,代码之间需要相互协作,互相依赖来完成功能,IOC技术恰好解决了这类问题,各个对象之间不需要直接关联,而是在需要用到对方时,交由IOC容器来管理对象之间的依赖关系。对于开发人员来说,只需要维护相对独立的各个对象代码即可。
IOC是一个过程,即对象定义其依赖关系,而其他与之配合的对象只能通过构造函数参数,工厂方法参数,或者在工厂方法构造或返回后在对象实例上设置属性来定义依赖关系,随后IOC容器在创建bean时,会注入这些依赖项,这个过程在指责上是反转的,也就是把原先代码中需要实现的对象创建依赖的代码反转给容器来帮忙实现,所以称之为“控制反转”
二、一个简单的注入案例
我们之前讲了一个非常简单的Spring的HelloWorld应用,但是很遗憾,在我们实际开发的任何一个场景肯定都要比HelloWorld要复杂的多。
例如我们之前所讲的message是写死的“HelloWorld”, 现在我们需要由一个MessageProvider类的providerMessage来获取这个信息,而不是固定写死的。
我们新建一个FileProviderMessage类来实现我们的ProviderMessage,说明他的实现是从文件中读取到了我们的message信息,并返回,代码如下
ProviderMessage 接口定义如下
package com.houpu.classn.dao;
public interface MessageProvider {
String getMessage();
}
FileProviderMessage实现了这个接口,并且实现方式是从文件里读取我们的message
package com.classn.dao.impl;
import com.classn.dao.MessageProvider;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
public class FileMessageProvider implements MessageProvider {
@Override
public String getMessage() {
Properties properties = new Properties();
System.out.println(this.getClass().getClassLoader().getResource("").getPath());
try {
properties.load(new FileInputStream("D:\\message.properties"));
return properties.getProperty("msg");
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
这个时候,我们应该修改我们的messageService的实现添加构造注入
package com.classn.service;
import com.classn.dao.MessageProvider;
import sun.plugin2.message.Message;
public class MessageService {
private MessageProvider provider;
public MessageService(MessageProvider provider){
this.provider = provider;
}
public void printMessage(){
System.out.println(provider.getMessage());
}
}
并且在我们的server,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="messageService" class="com.houpu.classn.service.MessageService">
<constructor-arg ref="messageProvider"/>
</bean>
<bean id="messageProvider" class="com.houpu.classn.dao.impl.FileMessageProvider"></bean>
</beans>
其中的及声明这种构造注入的关系,告诉Spring组件messageService将要将组件messageProvider当成构造函数的参数,经行注入
如果不使用我们的Spring的IOC/DI功能,那么我们的main程序肯定会变成如下形式
public class MyApplication {
public static void main(String[] args) {
FileMessageProvider fileMessageProvider = new FileMessageProvider();
MessageService messageService = new MessageService(fileMessageProvider);
messageService.printMessage();
}
}
这样会导致若干问题:
-
在MyApplication中指导了messageService的创建过程,一旦messageService的创建方式有改动,那么MyApplication和所有有messageService创建过程的类全部需要重新修改并且编译。
-
构造的过程中实例化的是一个具体的实现类,如果我的信息获取的方式发生了修改,则同样需要修改MyApplication类和所有引用messageService的代码,并且重新编译。
-
以上两点说明了我们的软件并不具备我们平时所强调的代码可扩展性,一旦进行功能扩展,关捋清这些依赖关系都是一个非常大的工作量
-
上述两点的重新编译,代表着该软件需要重新编译,部署,全部重新测试,这对于软件的稳定性来说也是非常致命的。
Spring引入了控制反转概念,讲对象的创建和依赖关系交由Spring工厂来实现和管理,大大的弱化了工程代码的对象之间的耦合关联,极大程度的弱化了组件之间的耦合度,提高了程序的可扩展性。
依赖注入是控制反转的实现方式之一
依赖注入一般分为两种方式,一种是构造器注入,一种是setter注入,我们之后详细解释两种注入方式的用法。