一、IoC的基本概念
IoC全称Inversion of Control,即控制反转。它的核心技术是DI (Dependency Injection),即依赖注入。
什么是IOC?打个比方,我们要做一个系统,为客户提供股票新闻。通常我们需要从不同的新闻社订阅新闻来源,然后通过批处理程序定时到指定的新闻服务器抓取最新的股票新闻,接着保存数据库,最后在前端显示。代码如下:
public class FXNewsProvider
{
private IFXNewsListener newsListener;
private IFXNewsPersister newPersistener;
public void getAndPersistNews()
{
String[] newsIds = newsListener.getAvailableNewsIds();
if(ArrayUtils.isEmpty(newsIds))
{
return;
}
for(String newsId : newsIds)
{
FXNewsBean newsBean = newsListener.getNewsByPK(newsId);
newPersistener.persistNews(newsBean);
newsListener.postProcessIfNecessary(newsId);
}
}
}
其中, FXNewsProvider 需要依赖 IFXNewsListener 来帮助抓取新闻内容,并依赖 IFXNews-
Persister 存储抓取的新闻。
假设默认使用道琼斯(Dow Jones)新闻社的新闻,那么我们相应地提供了 DowJonesNewsLis-
tener 和 DowJonesNewsPersister 两个实现。通常情况下,需要在构造函数中构造 IFXNewsProvider
依赖的这两个类:
public FXNewsProvider() {
this.newsListener = new DowJonesNewsListener();
this.newPersistener = new DowJonesNewsPersister();
}
以往,如果我们依赖于某个类或服务,最简单而有效的方式就是直接在类的构造函数中新建相应的依赖类。我们都是自己主动地去获取依赖的对象!
IoC则是在我们需要时将某个依赖对象送过来,它的反转,就反转在让你从原来的事必躬亲,转变为现在的享受服务。
二、依赖注入
被注入对象又是通过哪些什么来通知IoC为其提供适当服务的呢?其实就是依赖注入!依赖注入主要有两种方式:1、构造方法注入;2、setter方法注入
2.1 构造方法注入
顾名思义,构造方法注入,就是被注入对象可以通过在其构造方法中声明依赖对象的参数列表,让IoC容器知道它需要哪些依赖对象。对于前面例子中的 FXNewsProvider 来说,只要声明如下构造方法即可支持构造方法注入。
public FXNewsProvider(IFXNewsListener newsListner,IFXNewsPersister newsPersister)
{
this.newsListener = newsListner;
this.newPersistener = newsPersister;
}
IoC会检查被注入对象的构造方法,取得它所需要的依赖对象列表,进而为其注入相应的对象。同一个对象是不可能被构造两次的,因此,被注入对象的构造乃至其整个生命周期,应该是由IoC来管理的。构造方法注入方式比较直观,对象被构造完成后,即进入就绪状态,可以马上使用。
2.2 setter 方法注入
当前对象只要为其依赖对象所对应的属性添加setter方法,就可以通过setter方法将相应的依赖对象设置到被注入对象中。以 FXNewsProvider 为例,添加setter方法后如下所示。
public class FXNewsProvider
{
private IFXNewsListener newsListener;
private IFXNewsPersister newPersistener;
public IFXNewsListener getNewsListener() {
return newsListener;
}
public void setNewsListener(IFXNewsListener newsListener) {
this.newsListener = newsListener;
}
public IFXNewsPersister getNewPersistener() {
return newPersistener;
}
public void setNewPersistener(IFXNewsPersister newPersistener) {
this.newPersistener = newPersistener;
}
}
这样,外界就可以通过调用 setNewsListener 和 setNewPersistener 方法为 FXNewsProvider 对象注入所依赖的对象了。
setter方法注入虽不像构造方法注入那样,让对象构造完成后即可使用,但相对来说更宽松一些,可以在对象构造完成后再注入。
2.3 两种注入方式的比较
构造方法注入的优点就是,对象在构造完成之后,即已进入就绪状态,可以马上使用。缺点就是,当依赖对象比较多的时候,构造方法的参数列表会比较长。而通过反射构造对象的时候,对相同类型的参数的处理会比较困难,维护和使用上也比较麻烦。而且在Java中,构造方法无法被继承,无法设置默认值。对于非必须的依赖处理,可能需要引入多个构造方法,而参数数量的变动可能造成维护上的不便。
setter方法注入因为方法可以命名, 所以setter方法注入在描述性上要比构造方法注入好一些。
另外,setter方法可以被继承,允许设置默认值,而且有良好的IDE支持。缺点当然就是对象无
法在构造完成后马上进入就绪状态。
三、IOC的优点
不使用IOC看上去照样可以实现功能,但是如果需求发生变化了,比如需求追加了处理另一家新闻社的新闻来源时,问题就来了。因为我们之前没有用IoC,所以,现在的对象跟 DowJonesNewsListener 是绑定
的,我们无法重用这个类了。
而使用IoC后,我们却完全可以不做任何改动,因为不管是DowJones还是MarketWin24,对于我们的系统来说,处理逻辑实际上应该是一样的。因此,我们只要根据MarketWin24的新闻服务接口,为MarketWin24的 FXNewsProvider 提供相应的 MarketWin24NewsListener 注入就可以了,见以下代码:
FXNewsProvider dowJonesNewsProvider = ➥
new FXNewsProvider(new DowJonesNewsListener(),new DowJonesNewsPersister());
...
FXNewsPrivider marketWin24NewsProvider = ➥
new FXNewsProvider(new MarketWin24NewsListener(),new DowJonesNewsPersister());
...
IoC是一种可以帮助我们解耦各业务对象间依赖关系的对象绑定方式!