首页 > 代码库 > spring技术内幕读书笔记之IoC容器的学习

spring技术内幕读书笔记之IoC容器的学习

第一篇:概念和设计原理

IoC容器的实现作为Spring的核心内容之一非常有必要拿来研究一下

1、概念

IoC(Inversion of Control,控制反转)必须思考的问题:哪些方面的控制被反转了?

对于这个问题,Martin Flower给出结论是:依赖对象的获得被反转了。基于此,他为控制反转创造了一个更好的名字:依赖注入。

SpringIoC模块是这一思想的一种实现,IoC容器把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,降低了组件之间的耦合度,利于功能复用,方便进行测试。

2、IoC容器的设计思想与实现

SpringIoC容器设计中有两个主要的容器系列:

①  实现了BeanFactory接口的简单容器系列,此类容器实现了容器最基本的功能。

②  ApplicationContext应用上下文,视为容器的最高形态存在,在实现简单容器的基础上增加了许多面向框架的特性,对应用环境做了许多适配。

spring中IoC容器的设计可参考下图:

技术分享

接下来对这两类容器进行详细分析,看他们是如何设计并实现的。

首先是BeanFactory

BeanFactory作为Spring最底层的一个IOC容器,只具备了对Bean管理的最基本的功能。

BeanFactory的定义:

public interface BeanFactory {
    //用 & 区分通过容器来获取BeanFactory产生的对象和获取FactoryBean本身
  StringFACTORY_BEAN_PREFIX = "&";
  //根据name取得IoC容器中管理的bean
  ObjectgetBean(String name) throws BeansException;
  <T>T getBean(String name, Class<T> requiredType) throws BeansException;
  <T>T getBean(Class<T> requiredType) throws BeansException;
  ObjectgetBean(String name, Object... args) throws BeansException;
  //判断是否有指定名字的bean
  booleancontainsBean(String name);
  //判断指定名字的bean是否是单例
  booleanisSingleton(String name) throws NoSuchBeanDefinitionException;
  //判断指定名字的bean是否是prototype类型的
  booleanisPrototype(String name) throws NoSuchBeanDefinitionException;
  //判断指定名字的bean是否是特定的class类型
  booleanisTypeMatch(String name, Class<?> targetType) throwsNoSuchBeanDefinitionException;
  //获取指定名称bean的class类型
  Class<?>getType(String name) throws NoSuchBeanDefinitionException;
  //获取指定名字bean的所有别名
  String[]getAliases(String name);
}

从BeanFactory的定义中可以看到容器的基本管理功能有哪些。

使用XmlBeanFactory进一步分析此类容器是如何实现的,先看XmlBeanFactory是如何定义:

public class XmlBeanFactory extendsDefaultListableBeanFactory {
    privatefinal XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
  publicXmlBeanFactory(Resource resource) throws BeansException {
      this(resource,null);
  }
  publicXmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throwsBeansException {
    super(parentBeanFactory);
    this.reader.loadBeanDefinitions(resource);
  }
}

可以看到XmlBeanFactory继承了DefaultListableBeanFactory。

DefaultListableBeanFactory是AbstractAutowireCapableBeanFactory的子类,而AbstractAutowireCapableBeanFactory实现了AutowireCapableBeanFactory接口,正是AutowireCapableBeanFactory继承了BeanFactory。

DefaultListableBeanFactory包含了基本IoC容器所具有的重要功能,很多地方会用到,像ApplicationContext,在spring中实际上是把DefaultListableBeanFactory作为一个默认功能完整的IoC容器来使用

继续说XmlBeanFactory,根据名称我们可以知道,XmlBeanFactory除了实现基本功能外,还增加了读取以XML文件定义的BeanDefeinition的功能。但XML文件定义的文件信息的处理并不是由XmlBeanFactory直接完成,而是在XmlBeanFactory中初始化了一个XmlBeanDefinitionReader,靠reader对象来处理。另外在构造XmlBeanFactory时需要指定BeanDefinition的来源,此来源需要封装成spring中的Resource类,Resource是spring中用来封装I/O操作的类,比如BeanDefinition信息是以XML的形式定义,那么可以使用像ClassPathResource res = new ClassPathResource("beans.xml");的方法构造Resource,然后传递给XmlBeanFactory的构造器,这样IoC容器就可以定位到需要的BeanDefinition信息来完成容器的初始化和依赖注入过程。

ClassPathResource res = newClassPathResource("SpringBeans.xml");
XmlBeanFactory factory = newXmlBeanFactory(res);
User user =(User)factory.getBean("userBean");

这就是此类容器的设计与实现了,但实际应用中我们通常不会采用这类容器(XmlBeanFactory甚至已经Deprecated),一般会采用较高级的ApplicationContext容器。

在ApplicationContext的设计中,一方面它继承了BeanFactory体系中的ListableBeanFactory,AutowrieCapableBeanFactory,HirearchicableBeanFactory等接口,具备了BeanFactory的基本功能,另一方面它通过继承MessageSource,resourceLoader,ApplicationEventPublisher接口,让它拥有更高级的IoC容器特性。

使用FileSystemXmlApplicationContext这一常用类分析此类容器。

先看FileSystemXmlApplicationContext的定义:

public classFileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
    publicFileSystemXmlApplicationContext() {
  }
  ...
  publicFileSystemXmlApplicationContext(String[] configLocations, boolean refresh,ApplicationContext parent)throws BeansException {
      super(parent);
    setConfigLocations(configLocations);
    if(refresh) {
        refresh();
      }
  }
  @Override
  protectedResource getResourceByPath(String path) {
      if(path != null && path.startsWith("/")) {
        path= path.substring(1);
      }
    returnnew FileSystemResource(path);
  }
}

代码中省略了一些构造器。

ApplicationContext应用上下文的主要内容已经在基类AbstractXmlApplicationContext中实现,在FileSystemXmlApplicationContext中,作为一个具体的应用上下文,需要实现与它自身相关的两个功能:

一个功能是,如果应用直接使用FileSystemXmlApplicationContext,对实例化这个应用上下文的支持,同时启动IoC容器的refresh()过程,这个refresh会牵涉一些列复杂操作,而且不同的容器实现这些操作是相似的,所以封装在基类中,这里直接调用。也是由此方法启动IoC容器的初始化过程。

另一个功能是FileSystemXmlApplicationContext怎样从系统文件中加载XML的bean定义资源有关。不同的应用上下文实现对应着不同放入读取BeanDefinition的方式。重写的getResourceByPath可以说明这一点。

具体的refresh和getResourceByPath方法是如何调用的,以及像上面例子中xml文件如何处理的将在下一节IoC容器初始化中会有详细的过程讲解。

那么此类容器使用起来很容易:

ApplicationContext context = new FileSystemXmlApplicationContext("SpringBeans.xml");
User user = (User) context.getBean("userBean");

第二篇:IoC容器的初始化>>


spring技术内幕读书笔记之IoC容器的学习