Spring源码解析一 IOC容器的初始化

Spring源码解析1 IOC容器的初始化

参考《Spring技术内幕》一书:  

 IoC容器的基本接口是由BeanFactory来定义的,也就是说BeanFactory定义了IoC容器的最基本的形式,并且提供了  IoC容器所应该遵守的最基本的服务契约。BeanFactory只是一个接口类,并没有给出容器的具体实现。DefaultListableBeanFactory,XmlBeanFactory,ApplicationContext,FileSystemXmlBeanFactory,ClassPathXmlBeanFactory都实现了BeanFactory接口并且扩展了IoC容器的功能。

首先介绍BeanFactory:

 

public interface BeanFactory {      
     
    //这里是对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,      
    //如果需要得到工厂本身,需要转义             
    String FACTORY_BEAN_PREFIX = "&";      
     
     
    //这里根据bean的名字,在IOC容器中得到bean实例,这个IOC容器就是一个大的抽象工厂。      
    Object getBean(String name) throws BeansException;      
     
    //这里根据bean的名字和Class类型来得到bean实例,和上面的方法不同在于它会抛出异常:如果根据名字取得的bean实例的Class类型和需要的不同的话。      
    Object getBean(String name, Class requiredType) throws BeansException;      
     
    //这里提供对bean的检索,看看是否在IOC容器有这个名字的bean      
    boolean containsBean(String name);      
     
    //这里根据bean名字得到bean实例,并同时判断这个bean是不是单件      
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;      
     
    //这里对得到bean实例的Class类型      
    Class getType(String name) throws NoSuchBeanDefinitionException;      
     
    //这里得到bean的别名,如果根据别名检索,那么其原名也会被检索出来      
    String[] getAliases(String name);      
     
}    

 

 

用户使用容器时,可以使用转义字符'&'来得到FactoryBean本身,用来区分通过容器来获取FactoryBean产生的对象还是获取FactoryBean本身。在Spring中所有的Bean都是由BeanFactory来管理的,而对于FactoryBean,它是一个能产生或者修饰对象生成的工厂Bean

BeanFactory和FactoryBean:BeanFactory它指的是IoC容器的编程抽象,而FactoryBean指的是一个抽象工厂,对它的调用返回的是工厂产生的对象,而不是它本身。

 

 

 

 

 

我们先通过编程实现IoC容器:

 

public class UserBeanFatory {   
    public static void main(String[] args) {   
        //创建一个BeanFactory,这里使用DefaultListableBeanFactory,包含IoC容器的重要功能   
        DefaultListableBeanFactory factory=new DefaultListableBeanFactory();   
        /*  
         * 创建一个载入BeanDefinition的读取器,这里使用XmlBeanDefinitionReader来载入XML文件形式的  
         * BeanDefinition,使用一个回调配置给BeanFactory  
         */  
        XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(factory);   
        /*  
         * 创建Ioc配置文件的抽象资源,这个抽象资源中包含了BeanDefinition的定义信息  
         */  
        ClassPathResource res=new ClassPathResource("applicationContext-beans.xml");   
        /*  
         * 从定义好的资源位置读入配置信息,具体的解析过程是由XmlBeanDefinitionReader  
         * 来完成的。完成整个的载入与注册Bean定义之后,需要的IoC容器就建立起来了  
         */  
        reader.loadBeanDefinitions(res);   
  
        User user=(User)factory.getBean("user");   
        System.out.println(user.getUsername()+":"+user.getPassword());   
        //等价于   
        XmlBeanFactory xmlfactory=new XmlBeanFactory(new ClassPathResource("applicationContext-beans.xml"));   
        User xmluser=(User)factory.getBean("user");   
        System.out.println(xmluser.getUsername()+":"+xmluser.getPassword());   
        ApplicationContext ac=new FileSystemXmlApplicationContext("D:/java/kcsj/SourceXmpBeanFactory/src/applicationContext-beans.xml");   
        ac.getBean("user");   
    }   
  
}  

 

 

 

 由上面我们可以想到IoC 容器初始化分为三个步骤:

1 BeanDefinition的Resource定位

2 BeanDefinition的载入和解析

3 BeanDefinition的注册

 

我们先看BeanDefinition的Resource定位:

下面以FileSystemXmlApplicationContext为例,通过分析这个ApplicationContext的实现来看看它是怎样完成Resource的定位的。

ApplicationContext ac=new FileSystemXmlApplicationContext("D:/java/kcsj/SourceXmpBeanFactory/src/applicationContext-beans.xml");

 

 


 

我们首先看看FileSystemXmlApplicationContext的源码:

 

public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {   
      .....   
      public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)   
            throws BeansException {   
  
        super(parent);   
        setConfigLocations(configLocations);   
        if (refresh) {   
            refresh();   
        }   
     }  

 


}

 

在FileSystemXmlApplicationContext 的构造函数中完成了两部分功能:1是设置BeanDefinition的配置文件的路径,是的所有在配置文件中的BeanDefinition都能得到有效地处理;2 就是通过refresh()方法启动了IoC容器的初始化。

AbstractApplicationContext的refresh()方法源码解析:

public void refresh() throws BeansException, IllegalStateException {   
        synchronized (this.startupShutdownMonitor) {   
            // Prepare this context for refreshing.   
            prepareRefresh();   
  
            // Tell the subclass to refresh the internal bean factory.   
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();   
  
            // Prepare the bean factory for use in this context.   
            prepareBeanFactory(beanFactory);   
  
            try {   
                // Allows post-processing of the bean factory in context subclasses.   
                postProcessBeanFactory(beanFactory);   
  
                // Invoke factory processors registered as beans in the context.   
                invokeBeanFactoryPostProcessors(beanFactory);   
  
                // Register bean processors that intercept bean creation.   
                registerBeanPostProcessors(beanFactory);   
  
                // Initialize message source for this context.   
                initMessageSource();   
  
                // Initialize event multicaster for this context.   
                initApplicationEventMulticaster();   
  
                // Initialize other special beans in specific context subclasses.   
                onRefresh();   
  
                // Check for listener beans and register them.   
                registerListeners();   
  
                // Instantiate all remaining (non-lazy-init) singletons.   
                finishBeanFactoryInitialization(beanFactory);   
  
                // Last step: publish corresponding event.   
                finishRefresh();   
            }   
  
            catch (BeansException ex) {   
                // Destroy already created singletons to avoid dangling resources.   
                destroyBeans();   
  
                // Reset 'active' flag.   
                cancelRefresh(ex);   
  
                // Propagate exception to caller.   
                throw ex;   
            }   
        }   
    }  

 

 它包含了IoC容器的整个初始化的过程,包括:BeanFactory 的更新,初始化messagesource,配置和注册后置处理器,注册监听器和事件触发器,还有进行预实例化(non-lazy-init)的处理等等。它把资源的定位交给了obtainFreshBeanFactory方法:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {   
    refreshBeanFactory();   
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();   
    if (logger.isDebugEnabled()) {   
        logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);   
    }   
    return beanFactory;   
}  

 

 然后又通过调用抽象方法refreshBeanFactory,它的实现在AbstractRefreshableApplicaitonContext中:

protected final void refreshBeanFactory() throws BeansException {   
        if (hasBeanFactory()) {   
            destroyBeans();   
            closeBeanFactory();   
        }   
        try {   
            DefaultListableBeanFactory beanFactory = createBeanFactory();   
            beanFactory.setSerializationId(getId());   
            customizeBeanFactory(beanFactory);   
            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,如果建立则销毁并关闭BeanFactory,然后创建BeanFactory,这里创建的是DefaultListableBeanFactory,然后调用loadBeanDefinitions载入BeanDefinition的配置信息。接着我们去看loadBeanDefinitions方法的具体执行过程:

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {   
    // Create a new XmlBeanDefinitionReader for the given BeanFactory.   
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);   
  
    // Configure the bean definition reader with this context's   
    // resource loading environment.   
    beanDefinitionReader.setResourceLoader(this);   
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));   
  
    // Allow a subclass to provide custom initialization of the reader,   
    // then proceed with actually loading the bean definitions.   
    initBeanDefinitionReader(beanDefinitionReader);   
    loadBeanDefinitions(beanDefinitionReader);   
}  

 

 

这里它先创建一个BeanDefinition的Xml读取器,并且回调配置给BeanFactory,如同我们前面通过编程实现IoC容器的初始化,然后再转到loadBeanDefinitions(beanDefintionReader)中:

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {   
        Resource[] configResources = getConfigResources();   
        if (configResources != null) {   
            reader.loadBeanDefinitions(configResources);   
        }   
        String[] configLocations = getConfigLocations();   
        if (configLocations != null) {   
            reader.loadBeanDefinitions(configLocations);   
        }   
    }  

 

 它首先获得BeanDefinition的配置文件的资源,判断是否存在,如果存在在加载,然后获取配置文件的路径,判断是否存在,如果存在则加载。一种是从资源中加载,另一种是从给定的路径中加载。由于我们在没有显式的定义资源,我们只是给定了一个配置文件的路径,所以它会从路径加载。也就是调用reader.loadBeanDefinitions(configLocations)方法。

public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {   
    Assert.notNull(locations, "Location array must not be null");   
    int counter = 0;   
    for (String location : locations) {   
        counter += loadBeanDefinitions(location);   
    }   
    return counter;   
}  

 

 

然后继续调用loadBeanDefinitions(location)方法:

public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {   
        return loadBeanDefinitions(location, null);   
    }  

 

 再转到:loadBeanDefinitions(String location, Set<Resource> actualResources)方法中

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {   
        ResourceLoader resourceLoader = getResourceLoader();   
        if (resourceLoader == null) {   
            throw new BeanDefinitionStoreException(   
                    "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");   
        }   
  
        if (resourceLoader instanceof ResourcePatternResolver) {   
            // Resource pattern matching available.   
            try {   
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);   
                int loadCount = loadBeanDefinitions(resources);   
                if (actualResources != null) {   
                    for (Resource resource : resources) {   
                        actualResources.add(resource);   
                    }   
                }   
                if (logger.isDebugEnabled()) {   
                    logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");   
                }   
                return loadCount;   
            }   
            catch (IOException ex) {   
                throw new BeanDefinitionStoreException(   
                        "Could not resolve bean definition resource pattern [" + location + "]", ex);   
            }   
        }   
        else {   
            // Can only load single resources by absolute URL.   
            Resource resource = resourceLoader.getResource(location);   
            int loadCount = loadBeanDefinitions(resource);   
            if (actualResources != null) {   
                actualResources.add(resource);   
            }   
            if (logger.isDebugEnabled()) {   
                logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");   
            }   
            return loadCount;   
        }   
    }  

 

 Resource resource = resourceLoader.getResource(location);就是用来定位BeanDefinition的资源的,它会交给DefaultResourceLoader的getResource()方法处理:

public Resource getResource(String location) {   
    Assert.notNull(location, "Location must not be null");   
    if (location.startsWith(CLASSPATH_URL_PREFIX)) {   
        return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());   
    }   
    else {   
        try {   
            // Try to parse the location as a URL...   
            URL url = new URL(location);   
            return new UrlResource(url);   
        }   
        catch (MalformedURLException ex) {   
            // No URL -> resolve as resource path.   
            return getResourceByPath(location);   
        }   
    }   
}  

 

 最后它又调用getResourceByPath(location),它被FileSystemXmlApplicationContext中getResourceByPath()覆盖了,具体源码如下:

protected Resource getResourceByPath(String path) {   
    if (path != null && path.startsWith("/")) {   
        path = path.substring(1);   
    }   
    return new FileSystemResource(path);   
} 

 

 到这里为止,IoC容器的初始化的第一步骤已经完成了,总结可得BeanDefinition的Resource的定位是通过DefaultResourceLoader来getResource()方法来定位的,在getResource()中又调用getResourceByPath(),它被不同的BeanFactory覆盖。
 

 

 

 

 

 

1 楼 zhudaokun 2010-12-28  
呵呵……好帖,收藏一下