obtainFreshBeanFactory方法源碼跟蹤

看這篇文章以前能夠先了解以前的跟蹤流程,https://www.jianshu.com/p/4934233f0eadjava

代碼過寬,能夠shift + 鼠標滾輪 左右滑動查看web

AbstractApplicationContext類refresh()方法中的第二個調用方法obtainFreshBeanFactory()的跟蹤。spring

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        ...
        // Tell the subclass to refresh the internal bean factory.
        // 告知子類去刷新內部的 bean factory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        ···
 }

斷點進入跟蹤。數組

/**
     * Tell the subclass to refresh the internal bean factory.
     * @return the fresh BeanFactory instance
     *
     * 告知子類去刷新內部的 bean factory,返回刷新後的 bean factory 實例
     */
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        
         //刷新 bean factory,進入此方法查看
        refreshBeanFactory();
        
         //返回已被刷新、註冊了beandefinition的factory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

refreshBeanFactory(零)

refreshBeanFactory方法在子類AbstractRefreshableApplicationContext中實現,在AbstractApplicationContext中被定義。app

//1.刷新 bean factory
refreshBeanFactory();


//先看下這個方法的定義
/**
* Subclasses must implement this method to perform the actual configuration load.
* The method is invoked by {@link #refresh()} before any other initialization work.
* <p>A subclass will either create a new bean factory and hold a reference to it,
* or return a single BeanFactory instance that it holds. In the latter case, it will
* usually throw an IllegalStateException if refreshing the context more than once.
* @throws BeansException if initialization of the bean factory failed
* @throws IllegalStateException if already initialized and multiple refresh
* attempts are not supported
* 
* 子類必須實現這個方法去執行配置的加載。這個方法在其餘任何初始化工做以前被 refresh 方法所調用,
* 他的子類要麼建立一個新的 bean factory ,並拿到factory的引用;要麼返回一個
* 已有的單例 bean factory 實例。
* 在後一個狀況中,若是刷新這個 context 超過一次,那麼就會拋出非法狀態異常
*/
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;


/**
* This implementation performs an actual refresh of this context's underlying
* bean factory, shutting down the previous bean factory (if any) and
* initializing a fresh bean factory for the next phase of the context's lifecycle.
* 
* 這個實現對 context 底層的 bean factory 執行刷新操做,關閉之前的 bean factory(若是有),
* 而且爲 context 生命週期的下一個階段初始化一個新的 bean factory
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
    
    //此時沒有 bean factory,直接跳過
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        
        //1.建立一個 bean factory
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        
        //將 context 的 id 設置爲 bean factory 的序列化id,
        //並創建起id和該 bean factory 實例的映射關係
        beanFactory.setSerializationId(getId());
        
        //2.自定義內部的 bean factory
        customizeBeanFactory(beanFactory);
        
        //3.加載 beanDefinition
        loadBeanDefinitions(beanFactory);
        synchronized (this.beanFactoryMonitor) {
            
            // 新建立的工廠,其內部屬性就位後,被 context 拿到引用
            this.beanFactory = beanFactory;
        }
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

1.createBeanFactory

跟蹤標記1的方法ide

這個方法在AbstractRefreshableApplicationContext類中ui

//1.建立一個 bean factory
DefaultListableBeanFactory beanFactory = createBeanFactory();

/**
* Create an internal bean factory for this context.
* Called for each {@link #refresh()} attempt.
* <p>The default implementation creates a
* {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
* with the {@linkplain #getInternalParentBeanFactory() internal bean factory} of this
* context's parent as parent bean factory. Can be overridden in subclasses,
* for example to customize DefaultListableBeanFactory's settings.
*
* 爲 context 建立一個內部的 bean factory。
* 每一個refresh方法都會嘗試調用該方法
* 默認的實現是建立一個DefaultListableBeanFactory對象,利用getInternalParentBeanFactory方法
* 將 context 的 parent 的 bean factory ,做爲該 context 內部 bean factory 的 parent bean factory
* 該方法能夠被子類覆蓋,例如自定義DefaultListableBeanFactory的設定
*/
protected DefaultListableBeanFactory createBeanFactory() {
    return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}

getInternalParentBeanFactory方法在AbstractApplicationContext類中this

AbstractApplicationContext是AbstractRefreshableApplicationContext的父類url

/**
* Return the internal bean factory of the parent context if it implements
* ConfigurableApplicationContext; else, return the parent context itself.
* 
* 若是 parent context 實現了ConfigurableApplicationContext接口,
* 那麼返回 parent context 內部的 bean factory
* 不然返回 parent context 自身
*/
protected BeanFactory getInternalParentBeanFactory() {
    return (getParent() instanceof ConfigurableApplicationContext) ?
        ((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();
}

不論是 application context 仍是 bean factory,都有實現BeanFactory這個接口,因此 context 也能夠看作是一個特殊類型的 bean factory 。spa

2.customizeBeanFactory

跟蹤標記2.自定義內部的bean工廠

//2.自定義內部的 bean factory
customizeBeanFactory(beanFactory);


/**
* Customize the internal bean factory used by this context.
* Called for each {@link #refresh()} attempt.
* <p>The default implementation applies this context's
* {@linkplain #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}
* and {@linkplain #setAllowCircularReferences "allowCircularReferences"} settings,
* if specified. Can be overridden in subclasses to customize any of
* 
* 自定義內部的 bean factory 用於這個 context
* 每一個refresh方法都會嘗試調用該方法
* 默認實現應用這個context的setAllowBeanDefinitionOverriding方法和setAllowCircularReferences方法
* 去設置,若是有這兩個方法所需的參數的話。
* 能夠在子類中覆蓋該方法去自定義DefaultListableBeanFactory中的任何設定
*/
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
    if (this.allowBeanDefinitionOverriding != null) {
        
        //相同名稱的不一樣bean definition可否被重複註冊
        beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    if (this.allowCircularReferences != null) {
        
        //是否嘗試自動去解決兩個bean之間的循環引用問題
        beanFactory.setAllowCircularReferences(this.allowCircularReferences);
    }
}

3.loadBeanDefinitions

跟蹤標記3的方法
這個方法在AbstractRefreshableApplicationContext中被定義,在XmlWebApplicationContext中被實現

//3.加載 beanDefinition
loadBeanDefinitions(beanFactory);

//在AbstractRefreshableApplicationContext類中的定義
/**
* Load bean definitions into the given bean factory, typically through
* delegating to one or more bean definition readers.
* 
* 一般經過一個或者多個 bean definition readers 去加載 bean definitions 到指定的 bean factory
*/
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
    throws BeansException, IOException;

//在XmlWebApplicationContext類中的實現
/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
* 經過一個XmlBeanDefinitionReader加載bean definitions
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    
    // Create a new XmlBeanDefinitionReader for the given BeanFactory.
    // 爲指定的 BeanFactory 建立一個新的XmlBeanDefinitionReader
    // 3.1XmlBeanDefinitionReader的初始化須要瞭解下
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    // Configure the bean definition reader with this context's
    // resource loading environment.
    // 將context加載了資源的environment配置給bean definition reader
    beanDefinitionReader.setEnvironment(getEnvironment());
    
    // 將 ResourceLoader 換成 XmlWebApplicationContext 對象
    // XmlWebApplicationContext 實現了 ResourceLoader 接口
    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.
    
    // 容許子類提供reader的自定義初始化
    // 而後處理正在加載中的bean definitions
    // 此方法是空實現
    initBeanDefinitionReader(beanDefinitionReader);
    
    // 3.2加載beanDefinition操做
    loadBeanDefinitions(beanDefinitionReader);
}

3.1 XmlBeanDefinitionReader

跟蹤3.1XmlBeanDefinitionReader的初始化的過程

// 3.1XmlBeanDefinitionReader的初始化須要瞭解下
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

/**
* Create new XmlBeanDefinitionReader for the given bean factory.
* 
* 經過給定的 bean factory 建立一個新的 XmlBeanDefinitionReader
*/
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
    super(registry);
}

先看下幾個類的繼承關係圖。

XmlBeanDefinitionReader的構造入參接受的是BeanDefinitionRegistry,這是工廠默認實現DefaultListableBeanFactory所實現的接口

BeanDefinitionRegistry繼承圖.png

root web application context 的默認實現是 XmlWebApplicationContext,他實現了ApplicationContext,ApplicationContext有幾個上層接口比較重要。

ApplicationContext繼承圖.png

XmlBeanDefinitionReader調用了父類的構造並傳遞 bean factory,父類構造的註釋上作了不少說明:

/**
* Create a new AbstractBeanDefinitionReader for the given bean factory.
* <p>If the passed-in bean factory does not only implement the BeanDefinitionRegistry
* interface but also the ResourceLoader interface, it will be used as default
* ResourceLoader as well. This will usually be the case for
* {@link org.springframework.context.ApplicationContext} implementations.
* <p>If given a plain BeanDefinitionRegistry, the default ResourceLoader will be a
* {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
* <p>If the passed-in bean factory also implements {@link EnvironmentCapable} its
* environment will be used by this reader.  Otherwise, the reader will initialize and
* use a {@link StandardEnvironment}. All ApplicationContext implementations are
* EnvironmentCapable, while normal BeanFactory implementations are not.
*
*
* 經過給定的 bean factory 建立一個新的 AbstractBeanDefinitionReader。
* 若是傳入的 bean factory 不只僅實現了 BeanDefinitionRegistry 接口,
* 還實現了ResourceLoader接口,那麼 bean factory 還會被當作ResourceLoader資源加載器。
* 一般這種狀況的 bean factory 它每每是ApplicationContext的實現。
* 若是給定一個簡單的BeanDefinitionRegistry實現,那麼默認的ResourceLoader採用
* PathMatchingResourcePatternResolver。
* 若是傳入的 bean factory 還實現了EnvironmentCapable接口,
* 那麼這個 bean factory 的environment會被這個reader所使用。
* 否者這個reader初始化使用默認StandardEnvironment。
* 全部的 ApplicationContext 都實現了 EnvironmentCapable 接口,
* 可是普通的BeanFactory實現並無實現.
*/
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    
    //拿到 bean factory 的引用,解析後的beandefinition會放入 bean factory 中
    this.registry = registry;

    // Determine ResourceLoader to use.
    // 肯定ResourceLoader資源加載器使用哪個
    if (this.registry instanceof ResourceLoader) {
        this.resourceLoader = (ResourceLoader) this.registry;
    }
    else {
        
        // DefaultListableBeanFactory未實現ResourceLoader,因此走這個
        this.resourceLoader = new PathMatchingResourcePatternResolver();
    }

    // Inherit Environment if possible
    // 若是能夠的話拿到他的environment
    if (this.registry instanceof EnvironmentCapable) {
        this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
    }
    else {
        
        // DefaultListableBeanFactory未實現EnvironmentCapable,因此走這個
        // StandardEnvironment初始化的時候就會將systemEnvironment和systemProperties加載進來
        this.environment = new StandardEnvironment();
    }
}

這樣3.1XmlBeanDefinitionReader的初始化也就走完了。

3.2 loadBeanDefinitions

在XmlWebApplicationContext類中,跟蹤3.2標記方法

// 3.2加載beanDefinition操做
loadBeanDefinitions(beanDefinitionReader);

/**
* Load the bean definitions with the given XmlBeanDefinitionReader.
* <p>The lifecycle of the bean factory is handled by the refreshBeanFactory method;
* therefore this method is just supposed to load and/or register bean definitions.
* <p>Delegates to a ResourcePatternResolver for resolving location patterns
* into Resource instances.
* 
* 使用給定的XmlBeanDefinitionReader加載 bean definitions
* bean factory 的生命週期已經被refreshBeanFactory方法所處理
* 所以這個方法應該僅僅只是加載或者註冊 bean definitions
* 委派一個 ResourcePatternResolver 去將 location patterns 解析成資源實例
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
    
    //3.2.1返回 context 的 configLocations,若是沒有被指定則返回null
    //這裏web.xml只配置了一個,就是"classpath:spring/applicationContext.xml"
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
        for (String configLocation : configLocations) {
            
            //3.2.2加載解析beanDefinitions
            reader.loadBeanDefinitions(configLocation);
        }
    }
}

3.2.1 getConfigLocations

跟蹤標記3.2.1的方法

getConfigLocations 方法調用的是父類 AbstractRefreshableWebApplicationContext 的 getConfigLocations 方法

此方法又調用了更上一級父類 AbstractRefreshableConfigApplicationContext 的 getConfigLocations 方法。

//3.2.1返回 context 的配置路徑,若是沒有被指定則返回null
String[] configLocations = getConfigLocations();

//此方法在AbstractRefreshableWebApplicationContext中
@Override
public String[] getConfigLocations() {
    return super.getConfigLocations();
}

//此方法在AbstractRefreshableConfigApplicationContext類中
/**
* Return an array of resource locations, referring to the XML bean definition
* files that this context should be built with. Can also include location
* patterns, which will get resolved via a ResourcePatternResolver.
* <p>The default implementation returns {@code null}. Subclasses can override
* this to provide a set of resource locations to load bean definitions from.
*
* 返回一個資源路徑的數組,它參照了 context 應該構建的xml bean definition文件。
* 也能夠包含被ResourcePatternResolver解析的location patterns 。
* 默認的實現是返回一個null,子類能夠覆蓋並提供一個資源路徑的集合從中加載bean definitions
*/
protected String[] getConfigLocations() {
    
    //在web.xml文件中已經配置了contextConfigLocation屬性,
    //因此變量configLocations值爲classpath:spring/applicationContext.xml
    //順便看若是configLocations爲null,getDefaultConfigLocations()方法怎麼實現的
    return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
}

//此方法在XmlWebApplicationContext類中
/**
* The default location for the root context is "/WEB-INF/applicationContext.xml",
* and "/WEB-INF/test-servlet.xml" for a context with the namespace "test-servlet"
* (like for a DispatcherServlet instance with the servlet-name "test").
* 
* root context 默認路徑是"/WEB-INF/applicationContext.xml"
* 名稱空間爲"test-servlet"的上下文路徑爲"/WEB-INF/test-servlet.xml"
* 像DispatcherServlet實例就是用"test"的servlet-name
*/
@Override
protected String[] getDefaultConfigLocations() {
    if (getNamespace() != null) {
        
        // "/WEB-INF/" + 名稱空間 + ".xml"
        return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
    }
    else {
        
        // "/WEB-INF/applicationContext.xml"
        return new String[] {DEFAULT_CONFIG_LOCATION};
    }
}

3.2.2 loadBeanDefinitions

跟蹤標記3.2.2的方法,利用reader加載解析beanDefinitions

BeanDefinitionReader接口中對這個方法作了定義

AbstractBeanDefinitionReader中實現了這個方法

//3.2.2加載解析beanDefinitions
reader.loadBeanDefinitions(configLocation);

//在AbstractBeanDefinitionReader類中
/**
* Load bean definitions from the specified resource location.
* <p>The location can also be a location pattern, provided that the
* ResourceLoader of this bean definition reader is a ResourcePatternResolver.
* 
* 從指定的資源路徑加載bean definitions
* 若是bean definition reader的ResourceLoader資源加載器是ResourcePatternResolver
* 那麼這路徑也能夠是location pattern
*/
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
    return loadBeanDefinitions(location, null);
}

//在AbstractBeanDefinitionReader類中
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
    
    //這裏拿到的是 root web application context,ApplicationContext接口有繼承ResourceLoader接口
    ResourceLoader resourceLoader = getResourceLoader();
    if (resourceLoader == null) {
        throw new BeanDefinitionStoreException(
            "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
    }
    
    //ApplicationContext接口也有繼承ResourcePatternResolver接口
    if (resourceLoader instanceof ResourcePatternResolver) {
        
        // Resource pattern matching available.
        // 可使用Resource pattern作匹配
        try {
            
            // 3.2.2.1經過路徑獲取 資源處理器 數組
            Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
            
            // 3.2.2.2經過 資源處理器 加載 BeanDefinitions
            int loadCount = loadBeanDefinitions(resources);
            
            // 這裏傳過來是null,跳過
            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.
        // 只能加載絕對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;
    }
}
3.2.2.1 getResources

跟蹤標記 3.2.2.1的方法。

該方法實際上走的是AbstractApplicationContext的getResources方法,經過 context 的成員屬性resourcePatternResolver去解析資源,也就是PathMatchingResourcePatternResolver類的getResources方法

// 3.2.2.1經過路徑獲取 資源處理器 數組
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);

/**
* Resolve the given location pattern into Resource objects.
* <p>Overlapping resource entries that point to the same physical
* resource should be avoided, as far as possible. The result should
* have set semantics.
* 
* 解析指定的location pattern 生成資源對象
* 儘量的避免指向同一物理資源的多個資源項重複
* 結果應該具備語義
*/
@Override
public Resource[] getResources(String locationPattern) throws IOException {
    return this.resourcePatternResolver.getResources(locationPattern);
}

PathMatchingResourcePatternResolver實現了ResourcePatternResolver接口。在看他的實現前,先看下兩個常量的定義。

/**
* Pseudo URL prefix for all matching resources from the class path: "classpath*:"
* This differs from ResourceLoader's classpath URL prefix in that it
* retrieves all matching resources for a given name (e.g. "/beans.xml"),
* for example in the root of all deployed JAR files.
* @see org.springframework.core.io.ResourceLoader#CLASSPATH_URL_PREFIX
*
* 從類路徑中匹配全部資源的僞URL前綴:"classpath*:"
* 他和ResourceLoader的類路徑URL前綴("classpath:")有所不一樣
* 他經過指定名稱(例如 "/beans.xml"),檢索全部的匹配資源,像在根中的被部署的全部jar文件
*/
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

/** 
* Pseudo URL prefix for loading from the class path: "classpath:" 
*
* 從類路徑中加載的僞URL前綴:"classpath:" 
*/
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;



@Override
public Resource[] getResources(String locationPattern) throws IOException {
    Assert.notNull(locationPattern, "Location pattern must not be null");
    
    //由於web.xml中配置的是"classpath:" ,因此走第二個條件
    if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
        
        // a class path resource (multiple resources for same name possible)
        // 一個類路徑資源(同樣的名稱可能有多個資源)
        if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
            
            // a class path resource pattern
            // 一個類路徑的 resource pattern
            // 能進入這個條件,說明除去前綴的後面那一部分中,帶有*或者?等表達式
            return findPathMatchingResources(locationPattern);
        }
        else {
            
            // all class path resources with the given name
            // 給定名稱的全部類路徑資源
            return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
        }
    }
    else {
        
        // Only look for a pattern after a prefix here
        // (to not get fooled by a pattern symbol in a strange prefix).
        
        // 只查找前綴後的pattern,也就是"classpath:spring/applicationContext.xml"中的
        // "spring/applicationContext.xml"部分,(classpath可能帶*,後面的路徑也可能帶*)
        // 不要被在奇怪前綴中的pattern符號所迷惑
        int prefixEnd = locationPattern.indexOf(":") + 1;
        
        // 這裏的判斷和前面同樣,看後部分有沒有帶*或者?等Pattern
        if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
            
            // a file pattern
            // 匹配pattern的文件
            return findPathMatchingResources(locationPattern);
        }
        else {
            
            // a single resource with the given name
            
            //指定名稱的單一資源文件
            //web.xml中沒有用通配符,因此走這個。
            //getResourceLoader返回的是 root web application context,
            //getResource方法進的是DefaultResourceLoader類中,
            //DefaultResourceLoader是 root web application context 的上層父類,進入這個方法
            return new Resource[] {getResourceLoader().getResource(locationPattern)};
        }
    }
}


/**
* Return a Resource handle for the specified resource.
* The handle should always be a reusable resource descriptor,
* allowing for multiple {@link Resource#getInputStream()} calls.
* <p><ul>
* <li>Must support fully qualified URLs, e.g. "file:C:/test.dat".
* <li>Must support classpath pseudo-URLs, e.g. "classpath:test.dat".
* <li>Should support relative file paths, e.g. "WEB-INF/test.dat".
* (This will be implementation-specific, typically provided by an
* ApplicationContext implementation.)
* </ul>
* <p>Note that a Resource handle does not imply an existing resource;
* you need to invoke {@link Resource#exists} to check for existence.
* 
* 根據指定的資源返回一個資源處理器
* 這個處理器是一個可以被重複使用的資源描述符
* 容許多種輸入流的調用方式
* 必定支持徹底限定URLS,例如 "file:C:/test.dat".
* 必定支持類路徑僞URLS,例如 "classpath:test.dat".
* 應該支持相對文件路徑,例如 "WEB-INF/test.dat".
*(一般根據不一樣的ApplicationContext實現,這個方法的相應實現也不同)
* 注意,資源處理器並不意味着一個真實存在的資源,須要調用exists方法去檢測
*/
@Override
public Resource getResource(String location) {
    Assert.notNull(location, "Location must not be null");
    
    // 沒有協議解析器,跳過
    for (ProtocolResolver protocolResolver : this.protocolResolvers) {
        Resource resource = protocolResolver.resolve(location, this);
        if (resource != null) {
            return resource;
        }
    }

    if (location.startsWith("/")) {
        return getResourceByPath(location);
    }
    
    //走的這個
    else 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 url = new URL(location);
            return new UrlResource(url);
        }
        catch (MalformedURLException ex) {
            
            // No URL -> resolve as resource path.
            // 不是URL,仍是做爲資源路徑去解析
            return getResourceByPath(location);
        }
    }
}
3.2.2.2 loadBeanDefinitions

跟蹤標記3.2.2.2的方法

此方法在類AbstractBeanDefinitionReader中

// 3.2.2.2經過 資源處理器 加載 BeanDefinitions
int loadCount = loadBeanDefinitions(resources);

@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
    Assert.notNull(resources, "Resource array must not be null");
    int counter = 0;
    for (Resource resource : resources) {
        counter += loadBeanDefinitions(resource);
    }
    return counter;
}

counter += loadBeanDefinitions(resource); -> 此方法裏面的內容很是多,單獨開了一篇文章:

https://www.jianshu.com/p/a0cfaedf3fc5

接下來跟蹤prepareBeanFactory方法:

https://www.jianshu.com/p/3468118a31f9

總結

  • 已經有 BeanFactory ,摧毀相關Bean並關閉工廠
  • 1.建立一個 bean factory
  • 將 context 的 id 設置爲 bean factory 的序列化id,並創建起id和該 bean factory 實例的映射關係
  • 2.自定義內部的 bean factory
  • 3.加載 beanDefinition
  • 返回刷新後的 BeanFactory

——————————————————————————————————

  • 1
  • 建立 bean factory,默認 DefaultListableBeanFactory
  • 添加依賴忽略接口,這些接口不會被自動注入。一般由 application contexts 註冊這些以其餘方式解析的依賴。
  • 若是 application contexts 有 parent,將這個 parent 內部的 bean factory 或者 parent 自己做爲 bean factory 的parentBeanFactory,依據的條件是 parent 是不是 ConfigurableApplicationContext 的子類。

——————————————————————————————————

  • 2
  • 相同名稱的不一樣bean definition可否被重複註冊,默認爲true
  • 是否嘗試自動去解決兩個bean之間的循環引用問題,默認爲 true

——————————————————————————————————

  • 3
  • 爲指定的 BeanFactory 建立一個新的 XmlBeanDefinitionReader,並設置 environment、resourceLoader 屬性
  • 獲取spring配置文件路徑,若是web.xml中沒有配置,默認去/WEB-INF/下查找
  • 3.2.2 利用建立的Reader實例去解析配置文件路徑

——————————————————————————————————

  • 3.2.2
  • 解析配置文件路徑生成 Resource 對象。資源的僞URL前綴classpath只加載類路徑下的資源,classpath*檢索全部的匹配資源,包括jar文件。
  • 遍歷全部的Resource對象,一一解析生成 BeanDefinition
相關文章
相關標籤/搜索