本文是王福强所著<<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. 在Spring Boot框架下使用WebSocket实现消息推送

    Spring Boot的学习持续进行中.前面两篇博客我们介绍了如何使用Spring Boot容器搭建Web项目(使用Spring Boot开发Web项目)以及怎样为我们的Project添加HTTPS的 ...

  2. C实战:强大的程序调试工具GDB

    C实战:强大的程序调试工具GDB 1.基本调试 这里只列举最最常用的GDB命令. 1.1 启动GDB gdb program:准备调试程序.也可以直接进入gdb,再通过file命令加载. 1.2 添加 ...

  3. Openstack: MP-BIOS bug: 8254 timer not connected to IO-APIC

    Issue: After you import an linux image into openstack and run an instance of it, you may find that t ...

  4. Linux基础指令

    Linux基础指令 只写了最简单的一些文件操作,基本没有带参数 查看当前目录 pwd 跳转到某路径 cd 查看当前目录下的文件 ls ls -l // -l 查看详细信息 打开当前所在文件夹 open ...

  5. Android中ViewFlipper的使用详解

    说到android的左右滑动效果我们可以说是在每个应用上面都可以看到这样的效果,不管是微博,还是QQ等. 实现左右滑动的方式很多,有ViewPager(不过这个和需要android-support-v ...

  6. java学习路线图-----java基础学习路线图(J2SE学习路线图)

    安装JDK和开发软件跳过,网上太多了,不做总结,以下是我总结的学习路线图,欢迎补充. JAVA基础语法 注释,标识符命名规则及Java中的关键字 Java基本数据类型 Java运算符与表达式 Java ...

  7. Python中使用rrdtool结合Django进行带宽监控

    我们有个网关需要做下带宽监控,能获取这个数据的唯一方法就是登录到管理界面查看.然后咱就写了个模拟登录的爬虫,定时抓取数据用rrdtool存储,最后通过Django来展示.这里就涉及了python的rr ...

  8. Java基础---Java---网络编程---TCP的传输、客户端和服务端的互访、建立一个文本转换器、编写一个聊天程序

    演示TCP的传输的客户端和服务端的互访 需求:客户端给服务端发送数据,服务端收到后,给客户端反馈信息. 客户端: 1.建立Socket服务,指定要连接方朵和端口 2.获取Socket流中的输出流,将数 ...

  9. 初探linux子系统集之timer子系统(二)

    想着博客中还没有翻译过一篇文章,虽然英文水平有限,但是借助google翻译慢慢地翻译出一篇文章也是不错的选择.那就来学习下hrtimer的文档吧,翻译的略搓,可以直接跳过这篇,这里仅作为学习的过程!^ ...

  10. 【一天一道LeetCode】#136. Single Number

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Given a ...