第四章IOC概念
4.1-IOC概念
一、IOC的全称叫做Inversion of Control,中文通常翻译为“控制反转”。是Spring框架的核心。
(★注意:控制反转实现方式一般有两种,依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。因为依赖查找使用的相对较少,我也还没有做详细的了解。这里我就只介绍依赖注入的方式)
在面向对象的开发过程中,我们的项目中的每个类往往都有相互之间的关系,这就是它们之间的依赖。由于依赖关系越来越多所以就会造成相互间耦合程度的不断加深,会引发很多问题。如果我们把这种依赖关系交由第三方单独管理,那就相当于把这种依赖控制的权利交出去了,这中方式就是控制反转(或者叫依赖反转)。
二、下面我们通过代码的形式详细解释控制反转的具体表现形式。
1,最原始的依赖1.0方式:
创建一个“天猫”的商场类TMallMarket代码如下。该类提供了一个buy方法,可以从repertory中获取商品。
import java.util.HashMap;
import java.util.Map;
public class TMallMarket {
private static Map<String, Object> repertory = new HashMap<String, Object>();
static{
repertory.put("IPhone6s", "IPhone6s");
repertory.put("Mi4", "Mi4");
repertory.put("Milk", "milks");
}
public Object buy(String string) {
Object object = repertory.get(string);
return object;
}
}
然后在创建一个人的类People代码如下。该类拥有一个TMallMarket 的成员变量。也有一个buy方法,可以通过TMallMarket 的buy方法来获取商品。这里需要注意的是,在People初始化的构造函数中创建了TMallMarket 的对象。
public class People {
private TMallMarket iMarket;
public People() {
iMarket = new TMallMarket();
}
public Object buy(String goods) {
Object object = null;
object = iMarket.buy(goods);
return object;
}
}
下面创建我们的测试类MainTest。通过构建People对象,调用buy方法就可以从TMallMarket 商场购买想要的商品了。但是这里我们提出一个问题,如果我现在这个想从京东上买东西,该怎么办呢?这样就会很麻烦了,添加一个京东的商场还不行,还得重新构造People类。这样的话就很麻烦了,下面我们进入接口实现方式的依赖2.0版本。
public class MainTest {
public static void main(String[] args) {
/*最原始的方式*/
People people = new People();
Object object = people.buy("Mi4");
System.out.println(object);
}
}
2,用接口依赖的2.0方式:
这一次我们先创建一个“商场”的接口代码如下所示。所有集成该接口的类,都必须实现buy方法。
public interface IMarket {
Object buy(String string);
}
然后我们通过继承的方式创建TMallMarket类,实现基本与依赖1.0一样,只是这几集成了IMarket。
import java.util.HashMap;
import java.util.Map;
public class TMallMarket implements IMarket{
private static Map<String, Object> repertory = new HashMap<String, Object>();
static{
repertory.put("IPhone6s", "IPhone6s");
repertory.put("Mi4", "Mi4");
repertory.put("Milk", "milks");
}
public Object buy(String string) {
Object object = repertory.get(string);
return object;
}
}
再看一下我们的People类也做了有些改变代码如下。我们在构造方法那里,将自己构建商场的方式修改为传入接口的实现类。这样在更改商场时就比较方便了。
public class People {
private IMarket iMarket;
public People(IMarket iMarket) {
this.iMarket = iMarket;
}
public Object buy(String goods) {
Object object = null;
object = iMarket.buy(goods);
return object;
}
}
下面我们看一下测试类代码。在创建People对象时,传入了一个IMarket接口的实现类。也就是说,如果我们现在要使用京东商场的话,只需要将京东商场的对象传递过去就可以了。这样我们就解决了依赖1.0版本中不方便更换商场的问题。
public class MainTest {
public static void main(String[] args) {
/*接口实现依赖方式*/
IMarket iMarket = new TMallMarket();
People people = new People(iMarket);
Object object = people.buy("Mi4");
System.out.println(object);
}
}
3,介绍完上面的两种,我们现在进入依赖的3.0模式即IOC:
在IOC的案例中People、IMarket、TMallMarket 三个类没有改变和依赖2.0版本的一样。首先创建自己的IOC容器代码如下。在MyIOC中有一个people 的成员变量用来存放初始化容器时存放对象。startMyIOC方法相当于启动一个IOC容器,根据转入的name参数来选择为people 对象注入那个市场。getPeoPle获得我们想要好的people 对象。
(★注意:方法中有需要传入一个name参数,这里应该是一个可以灵活配置的文件。根据你的配置来选择为People对象注入那个市场。这里为了演示的简单能够直观的展示IOC的特点,使用转入一个name来替代读入文件的方式)
public class MyIOC {
public static People people = null;
public void startMyIOC(String name){
if ("TMallMarket".equals(name)) {
people = new People(new TMallMarket());
} else if ("JingDongMarket".equals(name)) {
people = new People(new JingDongMarket());
}
}
public Object getPeople() {
return people;
}
}
接下来在我们的测试类中看一看是如何使用我们自己定义的IOC。很明显,这种方式和我们演示spring的helloworld的时候是很相似的。看起来更加清楚,先初始化容器,在通过容器获取你需要的对象,然后使用对象。
在这个案例中,我们将People与IMarket匹配的事情,也就是相互依赖的关系完全的交给了MyIOC来处理。这就是IOC控制反转的核心,将相互的依赖(控制)进行了反转。初次看起来好像和依赖2.0的接口没有谁什么区别,是因为我们这里案例比较简单实现的也不完善。当我开始深入接触spring之后就会感受到IOC的强大。
public class MainTest {
public static void main(String[] args) {
/*IOC实现的依赖关系*/
//初始化IOC容器。
MyIOC myIOC = new MyIOC();
myIOC.startMyIOC("TMallMarket");
//通过IOC容器获取对象和使用对象。
People people = (People) myIOC.getPeople();
System.out.println(people.buy("Mi4"));
}
}
4.2-注入的方式
接着上一节的IOC依赖3.0版本,介绍一下关于注入的方式。
1,构造方法注入。
我们看MyIOC的startMyIOC方法是使用的构造器注入,即在创建people 对象时在构造器中注入相应的依赖。
public void startMyIOC(String name){
if ("TMallMarket".equals(name)) {
people = new People(new TMallMarket());
} else if ("JingDongMarket".equals(name)) {
people = new People(new JingDongMarket());
}
}
2,stter和getter方法注入。
首先我们改造一下People类代码如下所示。这里加入了setiMarket和getiMarket方法。提供了一个无参的构造器。接下我们在来为MyIOC添加一个新的启动方法。
public class People {
private IMarket iMarket;
public People() {
// TODO Auto-generated constructor stub
}
public People(IMarket iMarket) {
this.iMarket = iMarket;
}
public IMarket getiMarket() {
return iMarket;
}
public void setiMarket(IMarket iMarket) {
this.iMarket = iMarket;
}
public Object buy(String goods) {
Object object = null;
object = iMarket.buy(goods);
return object;
}
}
MyIOC新加的方法如下所示。创建people 对象后通过setiMarket方法注入相应的依赖。这里只是简单的介绍了以下两种注入的方式,还有一种接口注入由于其侵入性较强已不怎么使用。
关于构造注入的优点,在于创建对象的同时,所有的依赖都会同时创建完成可以马上使用。但是当依赖较多时构造方法的参数列表可能较长,而且构造方法不能被继承无法设置默认值。
对于stter和getter注入,其依赖的对象可以延后注入,在创建完对象后不能立即使用。但是stter和getter方法可以被继承,其依赖对象注入的时机也可以灵活选择。综合来说这种方式在一般情况下是最可取的。在spring可以更加清晰的了解这些注入方式的作用和效果这里就不再多讲了。
public void startMyIOCByStter(String name){
if ("TMallMarket".equals(name)) {
people = new People();
people.setiMarket(new TMallMarket());
} else if ("JingDongMarket".equals(name)) {
people = new People();
people.setiMarket(new JingDongMarket());
}
}