zoukankan      html  css  js  c++  java
  • Spring源码解析一:IOC容器设计

    一、IOC接口设计

    IOC容器设计的源码主要在spring-beans.jar、spring-context.jar这两个包中。IOC容器主要接口设计如下:

    这里的接口设计有两条主线:BeanFactory和ApplicationContext

    1、BeanFactory-->HierarchicalBeanFactory-->ConfigurableBeanFactory:这是BeanFactory的设计路线,BeanFactory定义了基本的IOC容器规范,HierarchicalBeanFactory中增加了getParentBeanFactory方法,具备了双亲IOC容器的管理功能;ConfigurableBeanFactory中新增一些配置功能。

    2、ApplicationContext应用上下文接口:继承了HierarchicalBeanFactory、ListableBeanFactory等BeanFactory的子接口,这条分支使得ApplicationContext具备了IOC容器的基本功能;在继承MessageSource、ApplicationEventPublisher等接口的时候,使得ApplicationContext这个简单的IOC容器添加了许多高级容器的特性。ApplicationContext的子接口有ConfigurableApplicationContext以及在WEB环境下使用的WebApplicationContext。

    二、BeanFactory的设计原理

    public abstract interface BeanFactory
    {
      public static final String FACTORY_BEAN_PREFIX = "&";
      
      public abstract Object getBean(String paramString)
        throws BeansException;
    
      public abstract <T> T getBean(String paramString, Class<T> paramClass)
        throws BeansException;
    
      public abstract <T> T getBean(Class<T> paramClass)
        throws BeansException;
    
      public abstract Object getBean(String paramString, Object[] paramArrayOfObject)
        throws BeansException;
    
      public abstract boolean containsBean(String paramString);
    
      public abstract boolean isSingleton(String paramString)
        throws NoSuchBeanDefinitionException;
    
      public abstract boolean isPrototype(String paramString)
        throws NoSuchBeanDefinitionException;
    
      public abstract boolean isTypeMatch(String paramString, Class<?> paramClass)
        throws NoSuchBeanDefinitionException;
    
      public abstract Class<?> getType(String paramString)
        throws NoSuchBeanDefinitionException;
    
      public abstract String[] getAliases(String paramString);
    }
    

     BeanFactory只是定义了IOC容器的基本轮廓,并没有给出容器的具体实现(这个后面详细介绍)。

    先来讨论下BeanFactory和FactoryBean之间的区别

    1、前者很好理解,就是Spring的一个类工厂,用它可以创建各种类型的Bean,最主要的方法就是getBean(String paramString)。而创建的各种类型的Bean中有一种比较特殊的Bean就是FactoryBean。

    2、Spring容器中管理里两种Bean,一种是标准的Java Bean,从容器中获取的是类本身的实例;另外一种就是FactoryBean即工厂Bean,从容器中获取Bean的时候,返回的并不是类的一个实例,而是工厂Bean中getObject方法返回的对象。工厂Bean必须实现接口FactoryBean

    工厂Bean---->SayHelloFactoryBeanImpl

    public class SayHelloFactoryBeanImpl implements FactoryBean
    {
    
        public Object getObject()
            throws Exception
        {
            return new UserBean();
        }
    
        public Class getObjectType()
        {
            return UserBean.class;
        }
    
        public boolean isSingleton()
        {
            return false;
        }
    }

    工厂Bean返回的对象:UserBean

    public class UserBean
    {
        public void show()
        {
            System.out.println("春天来了");
        }
    }

    Spring配置文件:

    <bean id="sayHelloBean" class="SayHelloFactoryBeanImpl"></bean>

    测试类:

        ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
                Object bean = ctx.getBean("sayHelloBean");
                Object bean1 = ctx.getBean("&sayHelloBean");
                System.out.println(bean);
                System.out.println(bean1);                

    执行结果:

    UserBean@7225790e
    SayHelloFactoryBeanImpl@54a097cc

    从正常的情况下,从容器中获取ID为“sayHelloBean”的对象应该是SayHelloFactoryBeanImpl但由于SayHelloFactoryBeanImpl实现了接口FactoryBean,这是一个工厂Bean,所以通过ID获取到的Bean是SayHelloFactoryBeanImpl类中getObject方法返回的对象。如果要获取FactoryBean自身的一个实例,必须通过&+BeanID的形式去获取。

    网上有许多对工厂Bean总结归纳,如:工厂Bean是实现了FactoryBean接口的bean  它不是一个简单的Bean 而是一个生产或修饰对象生成的工厂Bean。这里我先Mark一下:为什么Spring要设计这种类型的Bean。

    三、XmlBeanFactory的解读

    XmlBeanFactory是IOC容器系列最底层的实现,它继承自DefaultListableBeanFactory这个类。而后者是Spring中非常重要的一个类,它是Spring容器中一个基本产品,可以把它当做一个默认的功能完整的IOC容器来使用。

    XmlBeanFactory除了从DefaultListableBeanFactory继承到IOC容器基本功能之外,还新增了一些其他功能,从名称就可以猜测出来,它是一个可以读取以XML文件方式定义BeanDefinition的容器。

    XmlBeanFactory源码如下:

     1 public class XmlBeanFactory extends DefaultListableBeanFactory
     2 {
     3   private final XmlBeanDefinitionReader reader;
     4 
     5   public XmlBeanFactory(Resource resource)
     6     throws BeansException
     7   {
     8     this(resource, null);
     9   }
    10 
    11   public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)
    12     throws BeansException
    13   {
    14     super(parentBeanFactory);
    15 
    16     this.reader = new XmlBeanDefinitionReader(this);
    17 
    18     this.reader.loadBeanDefinitions(resource);
    19   }
    20 }

    实际上,实现XML读取功能并不是直接由XmlBeanFactory来完成的。而是由XmlBeanFactory内部定义的XmlBeanDefinitionReader来进行处理的。在构造XmlBeanFactory容器的时候,需要给出BeanDefinition的信息来源,而这个信息来源需要封装成Spring中的Resource类的形式给出。

    来看下一个基本的IOC容器的初始化过程:

    1、创建IOC配置文件的抽象资源,也就是源码中的Resource,这个Resource中包含了BeanDefinition的定义信息。

    2、通过构造函数创建一个BeanFactory。

    3、创建一个载入BeanDefinition的读取器,即源码中的reader。这里使用XmlBeanDefinitionReader来载入XML文件形式的BeanDefinition。

    4、调用reader的loadBeanDefinitions方法,来完成从Resource中载入BeanDefinition信息,从而完成IOC容器的初始化。

    我们可以将上面的源码做简化,使用编程式的方式来表达IOC容器的初始化:

    总结:DefaultListableBeanFactory是IOC容器的一个基类,XmlBeanFactory是在其基础上扩展而来的。而其他的IOC容器,例如ApplicationContext,它的实现原理和XmlBeanFactory类似,也是通过扩展DefaultListableBeanFactory来获取基本的IOC容器功能的。

    四、ApplicationContext的设计原理

    接口设计图:

    1、ApplicationContext继承接口ListableBeanFactory、HierarchicalBeanFactory,实现了IOC容器的基本功能。

    2、继承接口MessageSource:支持不同信息源,支持国际化的实现。

    3、继承接口ResourceLoader:支持该容器可以从不同I/O途径得到Bean的定义信息。

    4、继承接口ApplicationEventPublisher:在上下文中引入了事件机制。这些事件机制和Bean的生命周期结合为Bean的管理提供了便利。

    ApplicationContext增加了这些附加功能,使得基本IOC容器的功能更加丰富,所以建议在开发应用的时候使用ApplicationContext作为IOC容器的基本形式。

    ApplicationContext的设计原理

    以子类FileSystemXmlApplicationContext的实现为例说明其设计原理。接口设计图如下:

     

    这个接口设计中,ApplicationContext应用上下文的主要功能已经在FileSystemXmlApplicationContext的基类AbstractXmlApplicationContext中实现了,而FileSystemXmlApplicationContext作为一个具体的IOC容器,只需要实现和其本身相关的功能即可。

    源码片段:

     1   public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
     2     throws BeansException
     3   {
     4     super(parent);
     5     setConfigLocations(configLocations);
     6     if (refresh)
     7       refresh();
     8   }
     9 
    10   protected Resource getResourceByPath(String path)
    11   {
    12     if ((path != null) && (path.startsWith("/"))) {
    13       path = path.substring(1);
    14     }
    15     return new FileSystemResource(path);
    16   }
    17 }

    这里面有两个主要的方法:refresh()、getResourceByPath(String path)

    1、refresh涉及到IOC容器启动的一系列操作,由于这个启动过程对于不同类型的容器来说都是相似的,所以这个启动过程被封装在基类中,具体的容器只需要调用即可。refresh方法后面会有详细介绍。

    2、getResourceByPath这个方法是跟FileSystemXmlApplicationContext区别于其他具体容器的功能。通过这个方法可以让容器在文件系统中读取以XML形式存在的BeanDefinition。

  • 相关阅读:
    UBI系统原理分析【转】
    udhcpc命令【转】
    Device Tree(三):代码分析【转】
    Linux 中断详解 【转】
    ROS探索总结(三)——ROS新手教程【转】
    机器人操作系统(ROS)教程4:ROS的框架【转】
    你知道市面上机器人都用哪些操作系统吗【转】
    dump_stack调用过程【原创】
    #if、#ifdef、#if defined之间的区别【转】
    浅析firmware完整生存和使用流程 【转】
  • 原文地址:https://www.cnblogs.com/dongguacai/p/6429503.html
Copyright © 2011-2022 走看看