本文是王福强所著<<spring揭秘>>一书的读书笔记

ioc的基本概念

一个例子

我们看下面这个类,getAndPersistNews方法干了四件事

1 通过newsListener获得所有的新闻id;

2 通过newsListener,用新闻id获得新闻实体

3 用newPersistener存储新闻实体

4 再使用newsListener发布新闻

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);
    }

  }
}  

但是newsListener与newPersistener到底从什么地方来呢?

一般情况下或者说我们自己写代码的时候一般在FXNewsProvider的构造方法里生成newsListener与newPersistener。代码如下:

public FXNewsProvider()  {
  newsListener   = new DowJonesNewsListener();
  newPersistener = new DowJonesNewsPersister();
}  

我们分析一下上面的代码,如果对照我们现实生活,那就是我们在造房子的同时自己手工造出(通过new方式)家具。

当然还有可能,你可以去工厂,让他们给你生产。





从代码角度来说,上面的没有问题,还很简洁。

可是,之前是用的一家公司(例如新华社)提供的IFXNewsListener,IFXNewsPersister。如果我想用另一家公司(例如法新社)的IFXNewsListener,IFXNewsPersister怎么办?

方法1 新建一个类继承FXNewsProvider,在FXNewsProvider2的构造方法里使用法新社的IFXNewsListener,IFXNewsPersister,然后getAndPersistNews方法就引用父类的即可。

方法2 重新写一个类似的类,如FXNewsProvider2.....

之前的代码,我们可以理解为是FXNewsProvider自己去取所依赖的组件,那么一旦使用新的组件,我们的更新就会比较麻烦。





那么如果把主动的"取",改为被动地"接受"呢?

构造方法如下:

public FXNewsProvider(IFXNewsListener newsListner,IFXNewsPersister newsPersister)  {
  this.newsListener   = newsListner;
  this.newPersistener = newsPersister;
}
//使用DowJones家的新闻
FXNewsProvider dowJonesNewsProvider =
new FXNewsProvider(new DowJonesNewsListener(),new DowJonesNewsPersister());
//MarketWin24家的新闻
FXNewsPrivider marketWin24NewsProvider =
new FXNewsProvider(new MarketWin24NewsListener(),new DowJonesNewsPersister());

这样一来,你想用谁的就用谁的。因为是别人给你推送(注入)过来的嘛。

注入方式

有三种,接口注入,构造方法注入,setter方法注入。

构造方法注入上面已经介绍了。

接口注入现在基本已经不用了,大家不用理会。

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;
  }
} 

看上去太简单了,不是吗。

掌管大局的IoC Service Provider

我们第二章说了,让别人来来给我"推送"我所需要的组件。

那么这个别人到底是谁?

别人就是IoC Service Provider。

IoC Service Provider在这里是一个抽象出来的概念,它可以指代任何将IoC场景中的业务对象绑定到一起的实现方式。它可以是一段代码,也可以是一组相关的类,甚至可以是比较通用的IoC框架或者IoC容器实现。

我们可以认为下面这4行代码就是IoC Service Provider,因为它完成了任务----将IoC场景中的业务对象绑定到一起

IFXNewsListener newsListener = new DowJonesNewsListener();
IFXNewsPersister newsPersister = new DowJonesNewsPersister();
FXNewsProvider newsProvider = new FXNewsProvider(newsListener,newsPersister);
newsProvider.getAndPersistNews();  

IoC Service Provider的职责

1 生产对象

2 绑定对象间的依赖关系。

如何管理依赖关系

硬编码

IoContainer container = ...;
container.register(FXNewsProvider.class,new FXNewsProvider());
container.register(IFXNewsListener.class,new DowJonesNewsListener());
container.register(IFXNewsPersister.class,new DowJonesNewsPersister()); 

//setRelation这个方法是我自己写的 sping不会这么干的 但是大概能说明问题
container.setRelation(FXNewsProvider.class,newsListener,IFXNewsListener.class);
container.setRelation(FXNewsProvider.class,newPersistener,IFXNewsPersister.class);

FXNewsProvider newsProvider = (FXNewsProvider)container.get(FXNewsProvider.class);
newProvider.getAndPersistNews(); 

在书的第四章,有一个硬编码的列子:

public static void main(String[] args)  {
  DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
  BeanFactory container = (BeanFactory)bindViaCode(beanRegistry);
  FXNewsProvider newsProvider =
  (FXNewsProvider)container.getBean("djNewsProvider");
  newsProvider.getAndPersistNews();
} 

public static BeanFactory bindViaCode(BeanDefinitionRegistry registry) {
    AbstractBeanDefinition newsProvider =
    new RootBeanDefinition(FXNewsProvider.class,true);
    AbstractBeanDefinition newsListener =
    new RootBeanDefinition(DowJonesNewsListener.class,true);
    AbstractBeanDefinition newsPersister =
    new RootBeanDefinition(DowJonesNewsPersister.class,true); 

    // 将bean定义注册到容器中
    registry.registerBeanDefinition("djNewsProvider", newsProvider);
    registry.registerBeanDefinition("djListener", newsListener);
    registry.registerBeanDefinition("djPersister", newsPersister); 

    // 指定依赖关系
    // 1. 可以通过构造方法注入方式
    ConstructorArgumentValues argValues = new ConstructorArgumentValues();
    argValues.addIndexedArgumentValue(0, newsListener);
    argValues.addIndexedArgumentValue(1, newsPersister);
    newsProvider.setConstructorArgumentValues(argValues); 

    // 2. 或者通过setter方法注入方式
    MutablePropertyValues propertyValues = new MutablePropertyValues();
    propertyValues.addPropertyValue(new ropertyValue("newsListener",newsListener));
    propertyValues.addPropertyValue(new PropertyValue("newPersistener",newsPersister));
    newsProvider.setPropertyValues(propertyValues);
    // 绑定完成
    return (BeanFactory)registry;
}  

不是很难,大家应该能看懂。

配置文件方式

<bean id="newsProvider" class="..FXNewsProvider">
  <property name="newsListener">
   <ref bean="djNewsListener"/>
  </property>
  <property name="newPersistener">
   <ref bean="djNewsPersister"/>
  </property>
</bean> 

<bean id="djNewsListener"
  class="..impl.DowJonesNewsListener">
</bean>
<bean id="djNewsPersister"
  class="..impl.DowJonesNewsPersister">
</bean>  
...
container.readConfigurationFiles(...);
FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("newsProvider");
newsProvider.getAndPersistNews(); 

元数据方式(注解方式)

这种方式的代表实现是Google Guice。我们可以直接在类中使用元数据信息来标注各个对象之间的依赖关系,然后由Guice框架根据这些注解所提供的信息将这些对象组装后,交给客户端对象使用。

public class FXNewsProvider  {
  private IFXNewsListener  newsListener;
  private IFXNewsPersister newPersistener; 

  @Inject
  public FXNewsProvider(IFXNewsListener listener,IFXNewsPersister persister)    {
    this.newsListener   = listener;
    this.newPersistener = persister;
  } 

}  

通过构造方法上的 @Inject,Guice就知道这个用构造方法注入方式,当然具体注入哪个对象,还需要别的信息,在Guice中是由Module提供的

 public class NewsBindingModule extends AbstractModule  { 

  @Override
  protected void configure() {
   bind(IFXNewsListener.class).to(DowJonesNewsListener.class).in(Scopes.SINGLETON);
   bind(IFXNewsPersister.class).to(DowJonesNewsPersister.class).in(Scopes.SINGLETON);
  } 

}  

最后的使用

Injector injector = Guice.createInjector(new NewsBindingModule());
FXNewsProvider newsProvider = injector.getInstance(FXNewsProvider.class);
newsProvider.getAndPersistNews(); 

感谢glt

spring揭秘 读书笔记 一 IoC初探的更多相关文章

  1. spring揭秘读书笔记----spring的ioc容器之BeanFactory

    spring的ioc容器是一种特殊的Ioc Service Provider(ioc服务提供者),如果把普通的ioc容器认为是工厂模式(其实很相似),那spring的ioc容器只是让这个工厂的功能更强 ...

  2. spring揭秘 读书笔记 二 BeanFactory的对象注册与依赖绑定

    本文是王福强所著<<spring揭秘>>一书的读书笔记 我们前面就说过,Spring的IoC容器时一个IoC Service Provider,而且IoC Service Pr ...

  3. spring揭秘 读书笔记 二 BeanFactory的对象注冊与依赖绑定

    本文是王福强所著<<spring揭秘>>一书的读书笔记 我们前面就说过,Spring的IoC容器时一个IoC Service Provider,并且IoC Service Pr ...

  4. Spring揭秘 读书笔记 三 bean的scope与FactoryBean

    本书可作为王富强所著<<Spring揭秘>>一书的读书笔记  第四章 BeanFactory的xml之旅 bean的scope scope有时被翻译为"作用域&quo ...

  5. Spring揭秘读书笔记 八 数据访问异常体系

    这篇博客 来自spring揭秘一书的第十三章 为什么要有访问异常都有一个体系,这个我们得从DAO模式说起. DAO模式 任何一个系统,不管是一个最简单的小系统,还是大规模的系统,都得跟数据打交道,说白 ...

  6. spring揭秘读书笔记----ioc的基本概念

    在看ico概念之前,先想一下我们平常需要依赖某个类时会怎么做? 无非是在要用到的地方写如下代码: Person person = new Person(); //然后就可以用person对象来获取Pe ...

  7. Spring揭秘 读书笔记 五 容器的启动

    Spring的IoC容器所起的作用,就是生产bean,并维持bean间的依赖关系.它会以某种方式加载Configuration Metadata(通常也就是XML格式的配置信息),然后根据这些信息绑定 ...

  8. Spring揭秘 读书笔记 七 BeanFactory的启动分析

    首先,先看我自己画的BeanFactory启动时的时序图. 第一次接触时序图,可能有些地方画的不是很符合时序图的规则,大家只关注调用顺序即可. public static void main(Stri ...

  9. spring揭秘 读书笔记 六 bean的一生

    我们知道,Spring容器具有对象的BeanDefinition来保存该对象实例化时需要的数据. 对象通过container.getBean()方法是才会初始化该对象. BeanFactory 我们知 ...

随机推荐

  1. How to code like a hacker

    We are coding. Are we engineers? Are we programmers? Are we coder? No, I want to be a hacker! Many g ...

  2. Objective-C方法的实现

    Objective-C的方法被两种数据类型描述:一个是选择子(SEL),它用来描述方法的名称;另一个是实现(IMP),它用来描述方法被调用时实际执行的代码(它们基本上只是C函数的指针). 类似于SEL ...

  3. #pragma pack(x) CPU对齐

    编译器会尽量把成员对齐以提高内存的命中率.对齐是可以更改的,使用"#pragma pack(x)" 可以改变编译器的对齐方式. C++固有类型的对界取编译器对齐方式与自身大小中较小 ...

  4. Linux 环境下的一些常用命令(三)

    转载自 http://www.oschina.net/translate/20-advanced-commands-for-middle-level-linux-users 21. 命令: Find ...

  5. Android中的语言和字符串资源

    在任何情况下,从您的应用代码中提取 UI 字符串并将其存放在外部文件中都是个好办法.Android 在每个 Android 项目中都提供一个资源目录,从而简化了这一过程. 如果您是使用 Android ...

  6. springMVC源码分析--动态样式ThemeResolver(一)

    Spring MVC中通过ThemeSource接口来提供对动态更换样式的支持,并提供了ResourceBundleThemeSource这个具体实现类来提供通过properties配置文件对them ...

  7. XMPP(一)-openfire服务端的安装和搭建

    XMPP全称:可扩展通讯和表示协议 简介:可扩展通讯和表示协议 (XMPP) 可用于服务类实时通讯.表示和需求响应服务中的XML数据元流式传输.XMPP以Jabber协议为基础,而Jabber是即时通 ...

  8. Springmvc注解注入的简单demo

    今天看了注解注入觉得确实简化了xml配置,一般情况下Spring容器要成功启动的三大要件分别是:Bean定义信息,Bean实现类,以及spring本身.如果采取基于XML的配置,Bean信息和Bean ...

  9. 14 fragment传值

    两个fragment传值 方式一 布局文件代码: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/and ...

  10. Hibernate初体验及简单错误排除

    Hibernate是什么,有多少好处,想必查找这类博文的都知道,所以就不多说了.下面是我对Hibernate简单使用的一个小小的总结.与君(主要是刚入门的)共勉吧! 创建的顺序 创建Hibernate ...