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覆蓋。
 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章