本文是王福强所著<<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. IntelliJ IDEA下Git的配置与使用(命令行下)

    1. 安装Git并配置好Git 安装与配置参见Git与码云(Git@OSC)入门-如何在实验室和宿舍同步你的代码(1)中的2.在本机安装Git与3.1 配置git. 2. 创建远程仓库 在gitee. ...

  2. 【移动开发】targetSdkVersion的作用

    在AndroidMenifest.xml中,常常会有下面的语句: <uses-sdk android:minSdkVersion="4" android:targetSdkV ...

  3. 自定义view实现阻尼效果的加载动画

    效果: > 需要知识: 1. 二次贝塞尔曲线 2. 动画知识 3. 基础自定义view知识 先来解释下什么叫阻尼运动 阻尼振动是指,由于振动系统受到摩擦和介质阻力或其他能耗而使振幅随时间逐渐衰减 ...

  4. PHP学习(4)——数据类型

    PHP 支持 8 种原始数据类型. 四种标量类型:(标量类型即为基本类型) boolean(布尔型) integer(整型) float(浮点型,也称作 double) (由于历史原因,float也叫 ...

  5. Android系统对话框——自定义关闭

    Android系统对话框--自定义关闭 Dialog是我们在项目中经常用到的,5.x以后的Dialog也很好看,很安卓风,Android也给我们提供了新的包,低版本可以显示一样的效果.我们在使用的导入 ...

  6. 改进版getpass库

    编程伊始 正式实施 改进版 源码 以数字显示 以自定义分隔符delimiter显示 如何使用 下载及安装 在您的代码中使用 源码下载 总结 用过Linux的都知道,尤其是进行使用包管理软件类似于apt ...

  7. Redis中的关系查询

    本文对Redis如何保存关系型数据,以及如何对其匹配.范围.模糊查询进行举例讲解,其中模糊查询功能基于最新的2.8.9以后版本. 1 关系型数据的存储 以Staff对象为例,在关系型数据库或类似Gri ...

  8. Spark Scheduler模块源码分析之TaskScheduler和SchedulerBackend

    本文是Scheduler模块源码分析的第二篇,第一篇Spark Scheduler模块源码分析之DAGScheduler主要分析了DAGScheduler.本文接下来结合Spark-1.6.0的源码继 ...

  9. Gazebo與ROS版本說明

    使用哪种ROS / Gazebo版本的组合 介绍 本文档提供了有关将不同版本的ROS与不同版本的Gazebo结合使用的选项的概述.建议在安装Gazebo ROS包装之前阅读它.重要!简单的分析,快速和 ...

  10. GC真正的垃圾:强、软、弱、和虚 对象

    垃圾回收的基本思想就是判断一个对象是否可触及性,说白了就是判断一个对象是否可以访问,如果对象对引用了,说明对象正在被使用,如果发现对象没有被引用,说明对象已经不再使用了,不再使用的对象可以被回收,但是 ...