Spring IOC体系结构(一)

一、 BeanFactory

         Spring Bean的创建是典型的工厂模式,这一系列的Bean工厂,也即IOC容器为开发者管理对象间的依赖关系提供了很多便利和基础服务,在Spring中有许多的IOC容器的实现供用户选择和使用,其相互关系如下:

其中BeanFactory作为最顶层的一个接口类,它定义了IOC容器的基本功能规范,BeanFactory 有三个子类:ListableBeanFactoryHierarchicalBeanFactory AutowireCapableBeanFactory。但是从上图中我们可以发现最终的默认实现类是 DefaultListableBeanFactory,他实现了所有的接口。查阅这些接口的源码和说明发现,每个接口都有他使用的场合,它主要是为了区分在 Spring 内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。例如 ListableBeanFactory 接口表示这些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的是这些 Bean 是有继承关系的,也就是每个Bean 有可能有父 Bean。AutowireCapableBeanFactory 接口定义 Bean 的自动装配规则。这四个接口共同定义了 Bean 的集合、Bean 之间的关系、以及 Bean 行为.

最基本的IOC容器接口BeanFactory

1 public interface BeanFactory {    
2      
3      //对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,    
4      //如果需要得到工厂本身,需要转义           
5      String FACTORY_BEAN_PREFIX = "&"; 
6         
7      //根据bean的名字,获取在IOC容器中得到bean实例    
8      Object getBean(String name) throws BeansException;    
9    
10     //根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。    
11      Object getBean(String name, Class requiredType) throws BeansException;    
12     
13     //提供对bean的检索,看看是否在IOC容器有这个名字的bean    
14      boolean containsBean(String name);    
15     
16     //根据bean名字得到bean实例,并同时判断这个bean是不是单例    
17     boolean isSingleton(String name) throws NoSuchBeanDefinitionException;    
18     
19     //得到bean实例的Class类型    
20     Class getType(String name) throws NoSuchBeanDefinitionException;    
21     
22     //得到bean的别名,如果根据别名检索,那么其原名也会被检索出来    
23    String[] getAliases(String name);    
24     
 }

在BeanFactory里只对IOC容器的基本行为作了定义,根本不关心你的bean是如何定义怎样加载的。

      而要知道工厂是如何产生对象的,我们需要看具体的IOC容器实现,spring提供了许多IOC容器的实现。

比如XmlBeanFactory,ClasspathXmlApplicationContext等。其中XmlBeanFactory就是针对最基本的ioc容器的实现,

这个IOC容器可以读取XML文件定义的BeanDefinition(XML文件中对bean的描述)。

       ApplicationContext是Spring提供的一个高级的IoC容器,它除了能够提供IoC容器的基本功能外,还为用户提供了以下的附加服务。

从ApplicationContext接口的实现,我们看出其特点:

         1.  支持信息源,可以实现国际化。(实现MessageSource接口)

         2.  访问资源。(实现ResourcePatternResolver接口)

         3.  支持应用事件。(实现ApplicationEventPublisher接口)

二、IoC容器的初始化?

       IoC容器的初始化包括BeanDefinition的Resource定位载入注册这三个基本的过程。我们以ApplicationContext为例讲解,ApplicationContext系列容器也许是我们最熟悉的,因为web项目中使用的XmlWebApplicationContext就属于这个继承体系,还有ClasspathXmlApplicationContext等,其继承体系如下图所示:

下面我们分别简单地演示一下两种ioc容器的创建过程

1、XmlBeanFactory的IOC容器创建过程

//根据Xml配置文件创建Resource资源对象,该对象中包含了BeanDefinition的信息
 ClassPathResource resource =new ClassPathResource("application-context.xml");
//创建DefaultListableBeanFactory
 DefaultListableBeanFactory factory =new DefaultListableBeanFactory();
//创建XmlBeanDefinitionReader读取器,用于载入BeanDefinition。之所以需要BeanFactory作为参数,是因为会将读取的信息回调配置给factory
 XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(factory);
//XmlBeanDefinitionReader执行载入BeanDefinition的方法,最后会完成Bean的载入和注册。完成后Bean就成功的放置到IOC容器当中,以后我们就可以从中取得Bean来使用
 reader.loadBeanDefinitions(resource);

2、FileSystemXmlApplicationContext 的IOC容器创建过程

 ApplicationContext =new FileSystemXmlApplicationContext(xmlPath);

1、设置资源加载器和资源定位

 //静态初始化块,在整个容器创建过程中只执行一次 
static {
		 //为了避免应用程序在Weblogic8.1关闭时出现类加载异常加载问题,加载IoC容  
            //器关闭事件(ContextClosedEvent)类  
		ContextClosedEvent.class.getName();
	}
    public AbstractApplicationContext() {
		this.resourcePatternResolver = getResourcePatternResolver();
	}

	 //FileSystemXmlApplicationContext调用父类构造方法调用的就是该方法 
	public AbstractApplicationContext(ApplicationContext parent) {
        // 调用AbstractApplicationContext()
        // 即 this.resourcePatternResolver = getResourcePatternResolver();
		this(); 
		setParent(parent);
	}

    //获取一个Spring Source的加载器用于读入Spring Bean定义资源文件  
    protected ResourcePatternResolver getResourcePatternResolver() {
         // AbstractApplicationContext继承DefaultResourceLoader,也是一个S  
        //Spring资源加载器,其getResource(String location)方法用于载入资源  
		return new PathMatchingResourcePatternResolver(this);
	}

AbstractApplicationContext构造方法中调用PathMatchingResourcePatternResolver的构造方法创建Spring资源加载器:

public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {  
        Assert.notNull(resourceLoader, "ResourceLoader must not be null");  
        //设置Spring的资源加载器  
        this.resourceLoader = resourceLoader;  
}

在设置容器的资源加载器之后,接下来FileSystemXmlApplicationContet执行setConfigLocations方法通过调用其父类AbstractRefreshableConfigApplicationContext的方法进行对Bean定义资源文件的定位,该方法的源码如下:

  //处理单个资源文件路径为一个字符串的情况  
    public void setConfigLocation(String location) {  
       //String CONFIG_LOCATION_DELIMITERS = ",; /t/n";  
       //即多个资源文件路径之间用” ,; /t/n”分隔,解析成数组形式  
        setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));  
    }  

    //解析Bean定义资源文件的路径,处理多个资源文件字符串数组  
     public void setConfigLocations(String[] locations) {  
        if (locations != null) {  
            Assert.noNullElements(locations, "Config locations must not be null");  
            this.configLocations = new String[locations.length];  
            for (int i = 0; i < locations.length; i++) {  
                // resolvePath为同一个类中将字符串解析为路径的方法  
                this.configLocations[i] = resolvePath(locations[i]).trim();  
            }  
        }  
        else {  
            this.configLocations = null;  
        }  
    } 

通过这两个方法的源码我们可以看出,我们既可以使用一个字符串来配置多个Spring Bean定义资源文件,也可以使用字符串数组,即下面两种方式都是可以的:

a.    ClasspathResource res = new ClasspathResource(“a.xml,b.xml,……”);

多个资源文件路径之间可以是用” ,; /t/n”等分隔。

b.    ClasspathResource res = new ClasspathResource(newString[]{“a.xml”,”b.xml”,……});

至此,Spring IoC容器在初始化时将配置的Bean定义资源文件定位为Spring封装的Resource。

2、AbstractApplicationContext的refresh函数载入Bean定义过程:

Spring IoC容器对Bean定义资源的载入是从refresh()函数开始的,refresh()是一个模板方法,refresh()方法的作用是:在创建IoC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器。refresh的作用类似于对IoC容器的重启,在新建立好的容器中对容器进行初始化,对Bean定义资源进行载入

FileSystemXmlApplicationContext通过调用其父类AbstractApplicationContext的refresh()函数启动整个IoC容器对Bean定义的载入过程:

 public void refresh() throws BeansException, IllegalStateException {  
2        synchronized (this.startupShutdownMonitor) {  
3            //调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识  
4            prepareRefresh();  
5            //告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从  
6           //子类的refreshBeanFactory()方法启动  
7            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  
8            //为BeanFactory配置容器特性,例如类加载器、事件处理器等  
9            prepareBeanFactory(beanFactory);  
10            try {  
11                //为容器的某些子类指定特殊的BeanPost事件处理器  
12                postProcessBeanFactory(beanFactory);  
13                //调用所有注册的BeanFactoryPostProcessor的Bean  
14                invokeBeanFactoryPostProcessors(beanFactory);  
15                //为BeanFactory注册BeanPost事件处理器.  
16                //BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件  
17                registerBeanPostProcessors(beanFactory);  
18                //初始化信息源,和国际化相关.  
19                initMessageSource();  
20                //初始化容器事件传播器.  
21                initApplicationEventMulticaster();  
22                //调用子类的某些特殊Bean初始化方法  
23                onRefresh();  
24                //为事件传播器注册事件监听器.  
25                registerListeners();  
26                //初始化所有剩余的单态Bean.  
27                finishBeanFactoryInitialization(beanFactory);  
28                //初始化容器的生命周期事件处理器,并发布容器的生命周期事件  
29                finishRefresh();  
30            }  
31            catch (BeansException ex) {  
32                //销毁以创建的单态Bean  
33                destroyBeans();  
34                //取消refresh操作,重置容器的同步标识.  
35                cancelRefresh(ex);  
36                throw ex;  
37            }  
38        }  
39    }

refresh()方法的作用是:在创建IoC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器。refresh的作用类似于对IoC容器的重启,在新建立好的容器中对容器进行初始化,对Bean定义资源进行载入

refresh()方法主要为IoC容器Bean的生命周期管理提供条件 

 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {  
        //这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory()方法,具体实现调用子类容器的refreshBeanFactory()方法
         refreshBeanFactory();  
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();  
        if (logger.isDebugEnabled()) {  
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);  
        }  
        return beanFactory;  
    } 

Spring IoC容器载入Bean定义资源文件从其子类容器的refreshBeanFactory()方法启动。

AbstractApplicationContext子类的refreshBeanFactory()方法:

容器真正调用的是其子类AbstractRefreshableApplicationContext实现的    refreshBeanFactory()方法,方法的源码如下:

protected final void refreshBeanFactory() throws BeansException {  
       if (hasBeanFactory()) {
            //如果已经有容器,销毁容器中的bean,关闭容器  
           destroyBeans();  
           closeBeanFactory();  
       }  
       try {  
            //创建IoC容器  
            DefaultListableBeanFactory beanFactory = createBeanFactory();  
            beanFactory.setSerializationId(getId());  
            //对IoC容器进行定制化,如设置启动参数,开启注解的自动装配等  
            customizeBeanFactory(beanFactory);  
            //调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器  
            loadBeanDefinitions(beanFactory);  
            synchronized (this.beanFactoryMonitor) {  
                this.beanFactory = beanFactory;  
            }  
        } catch (IOException ex) {  
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);  
        }  
}

在这个方法中,先判断BeanFactory是否存在,如果存在则先销毁beans并关闭beanFactory,接着创建DefaultListableBeanFactory,并调用loadBeanDefinitions(beanFactory)装载bean定义。

3、AbstractRefreshableApplicationContext子类的loadBeanDefinitions方法:

AbstractRefreshableApplicationContext中定义了抽象的loadBeanDefinitions方法,

容器真正调用的是其子类AbstractXmlApplicationContext对该方法的实现,AbstractXmlApplicationContext的主要源码如下:

 public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {  
     ……  
     //实现父类抽象的载入Bean定义方法  
     @Override  
     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {  
         //创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容  器使用该读取器读取Bean定义资源  
         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  
         //为Bean读取器设置Spring资源加载器,AbstractXmlApplicationContext的  
         //祖先父类AbstractApplicationContext继承DefaultResourceLoader,因此,容器本身也是一个资源加载器  
        beanDefinitionReader.setResourceLoader(this);  
        //为Bean读取器设置SAX xml解析器  
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));  
        //当Bean读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制  
        initBeanDefinitionReader(beanDefinitionReader);  
        //Bean读取器真正实现加载的方法  
        loadBeanDefinitions(beanDefinitionReader);  
    }  
    //Xml Bean读取器加载Bean定义资源  
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {  
        //获取Bean定义资源的定位  
        Resource[] configResources = getConfigResources();  
        if (configResources != null) {  
            //Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位  
            //的Bean定义资源  
            reader.loadBeanDefinitions(configResources);  
        }  
        //如果子类中获取的Bean定义资源定位为空,则获取FileSystemXmlApplicationContext构造方法中setConfigLocations方法设置的资源  
        String[] configLocations = getConfigLocations();  
        if (configLocations != null) {  
            //Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位  
            //的Bean定义资源  
            reader.loadBeanDefinitions(configLocations);  
        }  
    }  
    //这里又使用了一个委托模式,调用子类的获取Bean定义资源定位的方法  
    //该方法在ClassPathXmlApplicationContext中进行实现,对于我们  
    //举例分析源码的FileSystemXmlApplicationContext没有使用该方法  
    protected Resource[] getConfigResources() {  
        return null;  
    }   ……  
}

Xml Bean读取器(XmlBeanDefinitionReader)调用其父类AbstractBeanDefinitionReader的 reader.loadBeanDefinitions方法读取Bean定义资源。由于我们使用FileSystemXmlApplicationContext作为例子分析,因此getConfigResources的返回值为null,因此程序执行reader.loadBeanDefinitions(configLocations)分支。

 

猜你喜欢

转载自blog.csdn.net/liangkun_java/article/details/81451472