Spring IOC——概述

IOC全称Inversion of Control, 中文通常翻译成“控制反转”,它还有一个别名叫做依赖注入(Dependency Injection)。IOC是一种设计思想,DI是IOC在Spring中的具体实现方式。

为什么需要IoC?IoC的具体意义是什么?它到底有什么独到之处?

传统对象依赖方式

为了更好地阐述IoC模式的概念,我们引入以下简单场景。

在FX项目中,经常需要近乎实时地为客户提供外汇新闻。通常情况下都是:

  • 先从不同的新闻社订阅新闻来源;
  • 然后通过批处理程序定时地到指定的新闻服务器抓取最新的外汇新闻,接着将这些新闻存入本地数据库;
  • 最后在FX系统的前台界面显示。

假如我们默认使用道琼斯新闻社的新闻,那么我们相应地提供了DowJonesNewsListener和DowJonesNewsPersister两个实现。通常情况下,需要在构造函数中构造IFXNewsProvider依赖的这两个类。这样FXNewsProvider类代码可能如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class FXNewsProvider{
//用于抓取新闻的监听器
private IFXNewsListener newsListener;
//用于持久化新闻存储器
private IFXNewsPersister newPersistener;
//构造函数,使用道琼斯新闻社的新闻
public FXNewsProvider(){
newsListener = new DowJonesNewsListener();
newPersistener = new DowJonesNewsPersister();
}
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);
}
}
}

看,这就是我们通常的做事方式!如果我们依赖于某个类或服务,最简单而有效的方式就是直接在类的构造函数中新建相应的依赖类。我们需要什么就主动去取什么。

IOC下的对象依赖

我们自己每次用到什么依赖对象都要主动地去获取,这是否真的必要?我们最终所要做的,其实就是直接调用依赖对象所提供的某项服务而已。只要用到这个依赖对象的时候,它能够准备就绪,我们完全可以不管这个对象是自己找来的还是别人送过来的。如果有人能够在我们需要时将某个依赖对象送过来,为什么还要大费周折地自己去折腾?

实际上,IoC就是为了帮助我们避免之前的“大费周折”,而提供了更加轻松简洁的方式。所谓控制反转,何为反转?就反转在让你从原来的事必躬亲,转变为现在的享受服务。所以,简单点儿说,IoC的理念就是,让别人为你服务!在下图中,也就是让IoC Service Provider来为你服务!

在IoC的场景中,二者之间通过IoC Service Provider来打交道,所有的被注入对象和依赖对象现在由IoC Service Provider统一管理。被注入对象需要什么,直接跟IoC Service Provider招呼一声,后者就会把相应的被依赖对象注入到被注入对象中,从而达到IoC Service Provider为被注入对象服务的目的。下图展示的两个场景,形象地说明了使用IOC模式前后的差别:

如何注入

作为被注入对象,要想让IoC Service Provider为其提供服务,并将所需要的被依赖对象送过来,需要通过某种方式通知对方。在Spring中依赖注入方式主要有3种:构造方法注入、setter方法注入和接口注入。

  1. 构造方法注入

    对于前面例子中的FXNewsProvider来说,只要声明如下构造方法(见代码清单2-3)即可支持构造方法注入:

    1
    2
    3
    4
    public FXNewsProvider(IFXNewsListener newsListner,IFXNewsPersister newsPersister) {
    this.newsListener = newsListner;
    this.newPersistener = newsPersister;
    }

    构造方法注入方式比较直观,对象被构造完成后,即进入就绪状态,可以马上使用

  2. setter方法注入

    对于JavaBean对象来说,通常会通过setXXX()和getXXX()方法来访问对应属性。所以,当前对象只要为其依赖对象所对应的属性添加setter方法,就可以通过setter方法将相应的依赖对象设置到被注入对象中。以FXNewsProvider为例,添加setter方法后代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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方法注入虽不像构造方法注入那样,让对象构造完成后即可使用,但相对来说更宽松一些,可以在对象构造完成后再注入。

  3. 接口注入

    被注入对象如果想要IoC ServiceProvider为其注入依赖对象,就必须实现某个接口。这个接口提供一个方法,用来为其注入依赖对象。这个接口会声明一个injectNewsListner方法(方法名随意),该方法的参数,就是所依赖对象的类型。这样,InjectionServiceContainer对象,即对应的IoC Service Provider就可以通过这个接口方法将依赖对象注入到被注入对象FXNewsProvider当中。

下面简单总结下这3种注入方法:

  • 接口注入:强制对象实现不必要的接口,比较繁琐,基本被弃用了。
  • 构造方法注入:其优点在于,对象构造完成后,依赖的对象就已经准备就绪,可以马上使用。
  • setter方法注入:其优点是setter方法可以被继承,允许设置默认值,而且有良好的IDE支持。缺点当然就是对象无
    法在构造完成后马上进入就绪状态。

IOC有啥好处

从主动获取依赖关系的方式转向IoC方式,不只是一个方向上的改变,简单的转变背后实际上蕴藏着更多的玄机。IOC不会对业务对象构成很强的侵入性,使用IoC后,对象具有更好的可测试性、可重用性和可扩展性,等等。下面通过一个例子,实际感受下。

还是上面抓取新闻的例子,假如有一天客户告诉我们,他们又搞定了一家新闻社MarketWin24,现在不仅需要抓取道琼斯的新闻,还要抓取Marketwin的新闻。在传统依赖对象模式中,FXNewsProvider和DowJonesNewsListener是绑定在一起的,因此就需要重写FXNewsProvider或者重新实现一个继承自FXNewsProvider的MarketWin24NewsProvider,或者干脆重新写一个类似的功能。

而在使用IOC后,面对同样的需求,我们却完全可以不做任何改动,就直接使用FXNewsProvider。代码如下图所示:

使用IOC的另一个好处就是更便于测试人员进行单元测试。还是这个例子,假如有道琼斯和Marketwin两个新闻来源,传统模式下我们无法抛开其中一个新闻监听器,去测试另外一个新闻监听器,因为这三个对象是耦合在一起的。在IOC模式下就不一样了,比如通过构造函数依赖注入,三个对象之间是解耦的,我们可以单独测试某一种新闻来源的监听器。

总之,IoC是一种可以帮助我们解耦各业务对象间依赖关系的对象绑定方式

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2022 Yin Peng
  • 引擎: Hexo   |  主题:修改自 Ayer
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信