1. 程式人生 > >Spring原始碼解讀-Spring IoC容器初始化之資源定位

Spring原始碼解讀-Spring IoC容器初始化之資源定位

**IoC初始化過程
首先spring IoC容器的初始化,要分成三大部分,BeanDefinition的
Resource定位、載入和註冊三個基本過程。
今天我要說的就是資原始檔的定位,IoC容器就像是一個大水桶,首先我要將水注入吧,我們要去哪找水呢,當然要從我們的給的配置檔案中了,小編寫了一段特別簡單的程式碼,然後將spring的原始碼,匯入,斷點除錯一步步跟進去,篇幅比較大,請諒解。**

BeanFactory fac = new ClassPathXmlApplicationContext("applicationContext.xml");

BeanFactory是基類介面,至於這個基類在哪,可以看下面這張圖。


這裡寫圖片描述
**上面這張圖,就是IoC容器的主要繼承關係。
斷點進入classPathXmlAppliactionContext時,我們會發現,所有的操作都是在下面這段程式碼中開始的:**

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {

        super(parent);
        setConfigLocations(configLocations);
        if
(refresh) { refresh(); } }

- 就是refresh這段程式碼開始的資源定位,載入和註冊的。看一下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進行判斷,判斷是否有水桶,或者水桶裡面是否有東西等等,我要保證我的水桶是乾淨的,大不了我換一個新的。

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);
        }
    }

後面則開始BeanDefinition的查找了(找水源),看下面程式碼:

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);
        }
    }

上面是兩種不同的方式來找水,一是我們src下的配置檔案,另一個則是資原始檔。我們是通過配置檔案來查詢的。

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
    //這裡取得ResourceLoader,使用的是DefaultResourceLoader
    ResourceLoader resourceLoader = getResourceLoader();
    if (resourceLoader == null) {
        throw new BeanDefinitionStoreException(
                "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
    }
    //針對Resource的路徑模式進行解析,可以使多個路徑,可以使多個檔案
    if (resourceLoader instanceof ResourcePatternResolver) {
        // Resource pattern matching available.
        try {
            //呼叫DefaultResourceLoader的getResource完成具體的Resource定位
            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的具體過程,看看defaultResourceLoader
public Resource getResource(String location) {
        Assert.notNull(location, "Location must not be null");
        //這裡處理帶有classpath標誌的resource
        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標識的Resource定位
                URL url = new URL(location);
                return new UrlResource(url);
            }
            catch (MalformedURLException ex) {
                // No URL -> resolve as resource path.
                //如果不是classpath又不是URL 把getResource的任務交給getResourceByPath,這個方法是protected方法,預設實現是得到一個classPathContextResource,這個方法常常用子類實現
                return getResourceByPath(location);
            }
        }
    }

程式碼跟到這,就開始返回了,返回到前面提到的refresh的目錄中,開始進行下一個階段的解析,BeanDefinition的載入和解析,下篇部落格會繼續講。