[精]Spring源碼深度解析系列(一) IOC容器的初始化詳解1

SpringFrameWork的核心是IOC容器的實現

1. IOC容器和依賴反轉模式

依賴反轉模式是為了解耦對象之間的依賴關系而誕生的一種模式或者說是一種思想,矯情來說,你甚至可以把它當成是一種情懷。

傳統來說,合作對象的引用以及依賴關系由具體對象來完成,會造成高度耦合和代碼可測試性降低,而在面向對象的系統中,對象封裝了數據和對數據的處理,對象的依賴關系常常體現在對數據和方法的依賴上.而這個時候如果將這些對象的依賴注入交給框架或者IOC容器來完成,就相當于將注入依賴的責任反轉交給了外界,而不再是由具體對象去操心這些事情了.

1.1 依賴控制反轉的實現方式

依賴控制反轉的實現方式有很多種,在Spring中,IOC容器便是實現這個模式的載體。它可以在對象生成或者是初始化時直接將數據注冊到對象中,也可以通過將對象引用注入到對象數據域中的方式來注入對方法調用的依賴。這種依賴注入是可以遞歸的,對象可以逐層注入。

2.1 Spring的IOC容器系列

BeanFactory和ApplicationContext都可以看作是容器的具體表現形式。

BeanFactory接口類型定義了IOC容是對器的基本功能規范,其余各式各樣的容器都是以這個接口定義的功能規范為基礎,
進行擴展以增加功能的。

我們可以看一下BeanFactory在繼承體系中的地位:

Spring的ioc容器系列.png

在這些Spring提供的基本ioc容器的接口定義和實現的基礎上,Spring通過定義BeanDefinition來管理基于Spring的應用中的各種對象以及它們之間的相互依賴關系.

BeanDefinition是我們對Bean定義的一種抽象,是對依賴反轉模式中管的對象依賴關系的一種抽象,是容器實現依賴反轉功能的核心數據結構。

2. IOC容器的實現:BeanFactory和ApplicationContext

2.21 BeanFactory對Ioc容器的功能定義

正如我們上面所說,BeanFactory定義了容器功能的基本規范,這個基本規范中包括了諸如getBean(),containsBean(),isSingleton(),isPrototype(),getType(),getAliases()此類的方法。

下面的代碼展示了BeanFactory接口供我們參考

public interface BeanFactory {

    /**
     * Used to dereference a {@link FactoryBean} instance and distinguish it from
     * beans <i>created</i> by the FactoryBean. For example, if the bean named
     * {@code myJndiObject} is a FactoryBean, getting {@code &myJndiObject}
     * will return the factory, not the instance returned by the factory.
     */
    String FACTORY_BEAN_PREFIX = "&";


    /**
     * Return an instance, which may be shared or independent, of the specified bean.
     * <p>This method allows a Spring BeanFactory to be used as a replacement for the
     * Singleton or Prototype design pattern. Callers may retain references to
     * returned objects in the case of Singleton beans.
     * <p>Translates aliases back to the corresponding canonical bean name.
     * Will ask the parent factory if the bean cannot be found in this factory instance.
     * @param name the name of the bean to retrieve
     * @return an instance of the bean
     * @throws NoSuchBeanDefinitionException if there is no bean definition
     * with the specified name
     * @throws BeansException if the bean could not be obtained
     */
    Object getBean(String name) throws BeansException;

    /**
     * Return an instance, which may be shared or independent, of the specified bean.
     * <p>Behaves the same as {@link #getBean(String)}, but provides a measure of type
     * safety by throwing a BeanNotOfRequiredTypeException if the bean is not of the
     * required type. This means that ClassCastException can't be thrown on casting
     * the result correctly, as can happen with {@link #getBean(String)}.
     * <p>Translates aliases back to the corresponding canonical bean name.
     * Will ask the parent factory if the bean cannot be found in this factory instance.
     * @param name the name of the bean to retrieve
     * @param requiredType type the bean must match. Can be an interface or superclass
     * of the actual class, or {@code null} for any match. For example, if the value
     * is {@code Object.class}, this method will succeed whatever the class of the
     * returned instance.
     * @return an instance of the bean
     * @throws NoSuchBeanDefinitionException if there is no such bean definition
     * @throws BeanNotOfRequiredTypeException if the bean is not of the required type
     * @throws BeansException if the bean could not be created
     */
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    /**
     * Return an instance, which may be shared or independent, of the specified bean.
     * <p>Allows for specifying explicit constructor arguments / factory method arguments,
     * overriding the specified default arguments (if any) in the bean definition.
     * @param name the name of the bean to retrieve
     * @param args arguments to use when creating a bean instance using explicit arguments
     * (only applied when creating a new instance as opposed to retrieving an existing one)
     * @return an instance of the bean
     * @throws NoSuchBeanDefinitionException if there is no such bean definition
     * @throws BeanDefinitionStoreException if arguments have been given but
     * the affected bean isn't a prototype
     * @throws BeansException if the bean could not be created
     * @since 2.5
     */
    Object getBean(String name, Object... args) throws BeansException;

    /**
     * Return the bean instance that uniquely matches the given object type, if any.
     * <p>This method goes into {@link ListableBeanFactory} by-type lookup territory
     * but may also be translated into a conventional by-name lookup based on the name
     * of the given type. For more extensive retrieval operations across sets of beans,
     * use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}.
     * @param requiredType type the bean must match; can be an interface or superclass.
     * {@code null} is disallowed.
     * @return an instance of the single bean matching the required type
     * @throws NoSuchBeanDefinitionException if no bean of the given type was found
     * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
     * @throws BeansException if the bean could not be created
     * @since 3.0
     * @see ListableBeanFactory
     */
    <T> T getBean(Class<T> requiredType) throws BeansException;

    /**
     * Return an instance, which may be shared or independent, of the specified bean.
     * <p>Allows for specifying explicit constructor arguments / factory method arguments,
     * overriding the specified default arguments (if any) in the bean definition.
     * <p>This method goes into {@link ListableBeanFactory} by-type lookup territory
     * but may also be translated into a conventional by-name lookup based on the name
     * of the given type. For more extensive retrieval operations across sets of beans,
     * use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}.
     * @param requiredType type the bean must match; can be an interface or superclass.
     * {@code null} is disallowed.
     * @param args arguments to use when creating a bean instance using explicit arguments
     * (only applied when creating a new instance as opposed to retrieving an existing one)
     * @return an instance of the bean
     * @throws NoSuchBeanDefinitionException if there is no such bean definition
     * @throws BeanDefinitionStoreException if arguments have been given but
     * the affected bean isn't a prototype
     * @throws BeansException if the bean could not be created
     * @since 4.1
     */
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;


    /**
     * Does this bean factory contain a bean definition or externally registered singleton
     * instance with the given name?
     * <p>If the given name is an alias, it will be translated back to the corresponding
     * canonical bean name.
     * <p>If this factory is hierarchical, will ask any parent factory if the bean cannot
     * be found in this factory instance.
     * <p>If a bean definition or singleton instance matching the given name is found,
     * this method will return {@code true} whether the named bean definition is concrete
     * or abstract, lazy or eager, in scope or not. Therefore, note that a {@code true}
     * return value from this method does not necessarily indicate that {@link #getBean}
     * will be able to obtain an instance for the same name.
     * @param name the name of the bean to query
     * @return whether a bean with the given name is present
     */
    boolean containsBean(String name);

    /**
     * Is this bean a shared singleton? That is, will {@link #getBean} always
     * return the same instance?
     * <p>Note: This method returning {@code false} does not clearly indicate
     * independent instances. It indicates non-singleton instances, which may correspond
     * to a scoped bean as well. Use the {@link #isPrototype} operation to explicitly
     * check for independent instances.
     * <p>Translates aliases back to the corresponding canonical bean name.
     * Will ask the parent factory if the bean cannot be found in this factory instance.
     * @param name the name of the bean to query
     * @return whether this bean corresponds to a singleton instance
     * @throws NoSuchBeanDefinitionException if there is no bean with the given name
     * @see #getBean
     * @see #isPrototype
     */
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    /**
     * Is this bean a prototype? That is, will {@link #getBean} always return
     * independent instances?
     * <p>Note: This method returning {@code false} does not clearly indicate
     * a singleton object. It indicates non-independent instances, which may correspond
     * to a scoped bean as well. Use the {@link #isSingleton} operation to explicitly
     * check for a shared singleton instance.
     * <p>Translates aliases back to the corresponding canonical bean name.
     * Will ask the parent factory if the bean cannot be found in this factory instance.
     * @param name the name of the bean to query
     * @return whether this bean will always deliver independent instances
     * @throws NoSuchBeanDefinitionException if there is no bean with the given name
     * @since 2.0.3
     * @see #getBean
     * @see #isSingleton
     */
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    /**
     * Check whether the bean with the given name matches the specified type.
     * More specifically, check whether a {@link #getBean} call for the given name
     * would return an object that is assignable to the specified target type.
     * <p>Translates aliases back to the corresponding canonical bean name.
     * Will ask the parent factory if the bean cannot be found in this factory instance.
     * @param name the name of the bean to query
     * @param typeToMatch the type to match against (as a {@code ResolvableType})
     * @return {@code true} if the bean type matches,
     * {@code false} if it doesn't match or cannot be determined yet
     * @throws NoSuchBeanDefinitionException if there is no bean with the given name
     * @since 4.2
     * @see #getBean
     * @see #getType
     */
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

    /**
     * Check whether the bean with the given name matches the specified type.
     * More specifically, check whether a {@link #getBean} call for the given name
     * would return an object that is assignable to the specified target type.
     * <p>Translates aliases back to the corresponding canonical bean name.
     * Will ask the parent factory if the bean cannot be found in this factory instance.
     * @param name the name of the bean to query
     * @param typeToMatch the type to match against (as a {@code Class})
     * @return {@code true} if the bean type matches,
     * {@code false} if it doesn't match or cannot be determined yet
     * @throws NoSuchBeanDefinitionException if there is no bean with the given name
     * @since 2.0.1
     * @see #getBean
     * @see #getType
     */
    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

    /**
     * Determine the type of the bean with the given name. More specifically,
     * determine the type of object that {@link #getBean} would return for the given name.
     * <p>For a {@link FactoryBean}, return the type of object that the FactoryBean creates,
     * as exposed by {@link FactoryBean#getObjectType()}.
     * <p>Translates aliases back to the corresponding canonical bean name.
     * Will ask the parent factory if the bean cannot be found in this factory instance.
     * @param name the name of the bean to query
     * @return the type of the bean, or {@code null} if not determinable
     * @throws NoSuchBeanDefinitionException if there is no bean with the given name
     * @since 1.1.2
     * @see #getBean
     * @see #isTypeMatch
     */
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    /**
     * Return the aliases for the given bean name, if any.
     * All of those aliases point to the same bean when used in a {@link #getBean} call.
     * <p>If the given name is an alias, the corresponding original bean name
     * and other aliases (if any) will be returned, with the original bean name
     * being the first element in the array.
     * <p>Will ask the parent factory if the bean cannot be found in this factory instance.
     * @param name the bean name to check for aliases
     * @return the aliases, or an empty array if none
     * @see #getBean
     */
    String[] getAliases(String name);

}

2.2.2 IOC容器XmlBeanFactory的實現原理

首先讓我們看一下XmlBeanFactory的繼承體系結構

XmlBeanFactory的繼承結構.png

BeanFactory接口提供了使用ioc容器的規范,在這個基礎上,Spring提供了符合這個ioc容器接口的一系列容器的實現供開發人員使用。

現在我們從這個容器系列的最底層實現XmlBeanFactory開始,這個容器實現只提供了最基本的IOC容器的功能,從名字可以看得出來,這個容器可以讀取以Xml形式定義的BeanDefinition.

XmlBeanFactory繼承自DefaultListableBeanFactory這個類,DefaultListableBeanFactory包含了ioc容器的重要
功能,XmlBeanFactory在繼承了DefaultListableBeanFactory容器的功能的同時,所增加的額外功能我們從名字就能看得出來,
它可以讀取以xml形式定義的BeanDefinition。

那么它實現xml讀取的功能是怎么實現的呢?
對xml文件定義的信息處理并不是由XmlBeanFactory這個類本身進行處理的。在XmlBeanFactory中,初始化了一個XmlBeanDefinitionReader對象,該對象用來進行xml文件信息的處理.

XmlBeanFactory的功能是建立在DefaultListableBeanFactory這個基本容器的基礎上的,在這個基本容器的基礎上實現了其他例如xml讀取的附加功能.在XmlBeanFactory的構造方法中需要用到Resource對象,對XmlBeanDefinitionReader 對象的初始化以及使用這個對象來完成loadBeanDefinition的調用,就是從這個調用啟動了從Resource載入BeanDefinitions的過程,這個過程同時也是ioc容器初始化的一個重要的部分.

我們可以看一下XmlBeanFactory的代碼實現

public class XmlBeanFactory extends DefaultListableBeanFactory {

    private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);


    /**
     * Create a new XmlBeanFactory with the given resource,
     * which must be parsable using DOM.
     * @param resource XML resource to load bean definitions from
     * @throws BeansException in case of loading or parsing errors
     */
    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, null);
    }

    /**
     * Create a new XmlBeanFactory with the given input stream,
     * which must be parsable using DOM.
     * @param resource XML resource to load bean definitions from
     * @param parentBeanFactory parent bean factory
     * @throws BeansException in case of loading or parsing errors
     */
    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        this.reader.loadBeanDefinitions(resource);
    }

}

參考XmlBeanFactory代碼的實現,我們用編程的方式來使用DefaultListableBeanFactory,從中我們可以看到ioc容器使用的一些基本的過程。

ClassPathResource res = new ClassPathResource("beans.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader (factory);
reader.loadBeanDefinitions(res);

這樣我們就可以通過factory對象來使用DefaultListableBeanFactory這個IOC容器了,在使用ioc容器時需要如下幾個步驟:

1.創建ioc配置文件的抽象資源,該抽象資源包含了BeanDefinition的定義信息
2.創建一個BeanFactory,這里使用DefaultListableBeanFactory
3.創建一個載入BeanDefinition的讀取器,這里使用XmlBeanDefinitionReader來載入XML文件形式的BeanDefinition 通過一個回調配置給BeanFactory
4.從定義好的資源位置讀取配置信息,具體的解析過程由XmlBeanDefinitionReader來完成。完成整個載入和注冊的Bean定義之后,需要的ioc容器就建立起來了,這個時候IOC容器就可以使用了.

2.2.3 ApplicationContext的特點

在Spring中系統已經為用戶提供了許多已經定義好的容器實現,相比于那些簡單擴展BeanFactory的基本ioc容器,開發人員常用的ApplicationContext除了能夠提供上述我們看到的容器的基本功能意外,還為用戶提供了許多附加服務,可以讓用戶更加方便的使用。

ApplicationContext的接口關系.png

1.支持不同的信息源

ApplicationContext擴展了MessageSource接口,這些信息源的擴展可以支持國際化的實現,為開發多語言版本的應用提供服務.

2.訪問資源

對ResourceLoader和Resource有所支持,這樣我們可以從不同的IO途徑來獲取bean的定義信息。
但是從接口關系上我們看不出來這一特性。一般來說,具體ApplicationContext都是繼承了DefaultResourceLoader的子類,這是因為DefaultResourceLoader是AbstractApplicationContext的基類。

3.支持應用事件

繼承了接口ApplicationEventPublisher,在上下文引入事件機制,為bean的管理提供了便利.

2.3 IOC容器的初始化

IOC容器的初始化過程: 包括BeanDefinition 的Resoure定位 、載入和注冊這三個基本的過程。

2.3.1 BeanDefinition的Resource定位

常見的相關的類有FileSystemXmlApplicationContext ...ClassPathXmlApplicationContext....以及XmlWebApplicationContext等。

FileSystemXmlApplicationContext : 從文件系統載入Resource
ClassPathXmlApplicationContext: 從Class Path載入Resource
XmlWebApplicationContext: 在Web容器中載入Resource

下面我們就以FileSystemXmlApplicationContext為例,通過分析這個ApplicationContext的實現來理解它的Resource定位的過程

FileSystemXmlApplicationContext的繼承體系.png

從這里可以看得出來FileSystemXmlApplicationContext已經通過繼承AbstractApplicationContext具備了ResourceLoader讀入以Resource定義的BeanDefinition的能力。因為AbstractApplicationContext的基類是DefaultResourceLoader

下面我們來看FileSystemXmlApplicationContext的實現

public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {

    /**
     * Create a new FileSystemXmlApplicationContext for bean-style configuration.
     * @see #setConfigLocation
     * @see #setConfigLocations
     * @see #afterPropertiesSet()
     */
    public FileSystemXmlApplicationContext() {
    }

    /**
     * Create a new FileSystemXmlApplicationContext for bean-style configuration.
     * @param parent the parent context
     * @see #setConfigLocation
     * @see #setConfigLocations
     * @see #afterPropertiesSet()
     */
    public FileSystemXmlApplicationContext(ApplicationContext parent) {
        super(parent);
    }

    /**
     * Create a new FileSystemXmlApplicationContext, loading the definitions
     * from the given XML file and automatically refreshing the context.
     * @param configLocation file path
     * @throws BeansException if context creation failed
     */
     //這個構造函數的configLocation包含的是BeanDefinition所在的文件的路徑.
    public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[] {configLocation}, true, null);
    }

    /**
     * Create a new FileSystemXmlApplicationContext, loading the definitions
     * from the given XML files and automatically refreshing the context.
     * @param configLocations array of file paths
     * @throws BeansException if context creation failed
     */
     //這個構造函數允許configLocation包含多個BeanDefinition所在的文件的路徑.
    public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
        this(configLocations, true, null);
    }

    /**
     * Create a new FileSystemXmlApplicationContext with the given parent,
     * loading the definitions from the given XML files and automatically
     * refreshing the context.
     * @param configLocations array of file paths
     * @param parent the parent context
     * @throws BeansException if context creation failed
     */
    //這個構造函數在允許configLocation包含多個BeanDefinition所在的文件的路徑的同時,還允許指定自己雙親的ioc容器.
    public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
        this(configLocations, true, parent);
    }

    /**
     * Create a new FileSystemXmlApplicationContext, loading the definitions
     * from the given XML files.
     * @param configLocations array of file paths
     * @param refresh whether to automatically refresh the context,
     * loading all bean definitions and creating all singletons.
     * Alternatively, call refresh manually after further configuring the context.
     * @throws BeansException if context creation failed
     * @see #refresh()
     */
     
    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
        this(configLocations, refresh, null);
    }

    /**
     * Create a new FileSystemXmlApplicationContext with the given parent,
     * loading the definitions from the given XML files.
     * @param configLocations array of file paths
     * @param refresh whether to automatically refresh the context,
     * loading all bean definitions and creating all singletons.
     * Alternatively, call refresh manually after further configuring the context.
     * @param parent the parent context
     * @throws BeansException if context creation failed
     * @see #refresh()
     */
     //在對象的初始化過程中,調用refresh函數載入BeanDefinition,這個refresh啟動了BeanDefinition的載入過程
    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {

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


    /**
     * Resolve resource paths as file system paths.
     * <p>Note: Even if a given path starts with a slash, it will get
     * interpreted as relative to the current VM working directory.
     * This is consistent with the semantics in a Servlet container.
     * @param path path to the resource
     * @return Resource handle
     * @see org.springframework.web.context.support.XmlWebApplicationContext#getResourceByPath
     */
     //這是應用于文件系統的Resource的實現,通過構造一個FileSystemResource來得到一個在文件系統定位的BeanDefinition
     //這個getResourceByPath是在BeanDefinitionReader的loadBeanDefinition中被調用的,采用了魔板模式,具體的定為實現由子類來完成.
     
    
    @Override
    protected Resource getResourceByPath(String path) {
        if (path != null && path.startsWith("/")) {
            path = path.substring(1);
        }
        return new FileSystemResource(path);
    }

}

在FileSystemXmlApplicationContext 中,我們可以看得出來主要實現了兩個重要的功能
一個是在構造函數中,對configLocation進行處理,使得所有配置在文件系統中的以xml形式存在的BeanDefinition都能得到有效的處理,比如實現了getResourceByPath.
另一方面對于ioc容器功能的相關實現,這里沒有涉及,因為它繼承了AbstractXmlApplicationContxt

在這里我門最需要明白的一點是,在構造函數中通過refresh來啟動了容器的初始化。

接下來我們可以分析一下整個BeanDefinition資源定位的過程,這個定位的過程是由refresh觸發的

getResourceByPath的調用關系.png

看了上面的調用過程我們可能會比較好奇,這個FileSystemXmlApplicationContext 在什么地方定義了BeanDefinition的讀入器BeanDefinitionReader來完成BeanDefinition信息的讀入呢?
我們之前有分析過,在IOC容器的初始化過程中,BeanDefinition資源的定位讀入和注冊過程都是分開進行的.
關于BeanDefinitionReader的配置,我們可以到FileSystemXmlApplicationContext的基類AbstractRefreshableApplicationContext中看看它們的實現.

下面分析AbstractRefreshableApplicationContext對容器的初始化 代碼部分


    @Override
    protected final void refreshBeanFactory() throws BeansException {
    //這里是判斷,如果已經建立了BeanFactory,則銷毀并關閉該BeanFactory
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
        //這里是創建并設置持有DefaultListableBeanFactory的地方
        //同時調用loadBeanDefinition再載入BeanDefinition的信息
            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);
        }
    }

  //這個是在上下文中創建DefaultListableBeanFactory的地方,而getInternalParentBeanFactory的具體實現可以參看   
  //AbstractApplicationContext中的實現,會根據容器中已有的雙親ioc容器生成DefaultListableBeanFactory的雙親ioc容器

    protected DefaultListableBeanFactory createBeanFactory() {
        return new DefaultListableBeanFactory(getInternalParentBeanFactory());
    }
 
   //這里是使用BeanDefinitionReader載入Bean定義的地方,因為允許有許多種載入方式,這里通過一個抽象函數把具體的實現委托給子類完成
    protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
            throws BeansException, IOException;
            
    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.
          //調用DefaultResourceLoader的getResource完成具體的Resource定位
            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的具體過程,我們可以看看DefaultResourceLoader是怎樣完成的
 
 
    @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);
        }
        //這里處理帶有classpath標識的Resource定位
        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標識的Resource定位
                URL url = new URL(location);
                return new UrlResource(url);
            }
            catch (MalformedURLException ex) {
                // No URL -> resolve as resource path.
                //如果既不是classpath也不是url標識的Resource定位,則把getResource的重任交給getResourceByPath
                //這個方法是一個protected方法,默認的實現是得到一個ClassPathContextResource,這個方法常常會用子類來實現.
                return getResourceByPath(location);
            }
        }
 

前面我們看到的getResourceByPath會被子類FileXmlApplicationContext實現,該方法返回的是一個FileSystemResource對象,通過這個對象Spring可以進行相關的IO操作,完成BeanDefinition的定位,它實現的就是對path進行分析,然后生成一個FileSystemResource對象返回。

   //FileXmlApplicationContext生成Resource對象
    @Override
    protected Resource getResourceByPath(String path) {
        if (path != null && path.startsWith("/")) {
            path = path.substring(1);
        }
        return new FileSystemResource(path);
    }

如果是其他的ApplicationContext,那么對應會生成其他種類的Resource,比如ClassPathResource ....ServletContextResource等.
我們看一下Resource類的繼承關系

Resource的定義和繼承關系.png

通過對前面的分析,我們以FileSystemXmlApplicationContext的實現原理為例子,了解了Resource定位問題的解決方案,即以FileSystem方式存在的Resource的定位實現。在BeanDefinition定位完成的基礎上,就可以通過返回的Resource對象來進行BeanDefinition的載入了。在定位過程完成以后,為BeanDefinition的載入創造了IO操作的條件,但是具體的數據還沒有開始讀入,這些數據的讀入將在我們接下來看到的BeanDefinition的載入和解析完成.

2.3.2 BeanDefinition的載入和解析

對于ioc容器來說,BeanDefinition的載入過程相當于把我們定義的BeaDefinition在ioc容器中轉化成一個Spring內部表示的數據結構的過程.
IOC容器對Bean的管理和依賴注入功能的實現,是通過對其持有的BeanDefinition進行各種相關的操作來完成的.這些BeanDefinition數據在ioc容器里面通過一個HashMap來保持和維護.

我們從DefaultListableBeanFactory來入手看看IOC容器是怎樣完成BeanDefinition的載入的..

為了了解這一點,我們先回到ioc容器的初始化入口refresh方法,這個方法最初是在FileSystemXmlApplicationContext的構造函數被調用的,它的調用就意味著容器的初始化或數據更新,這些初始化和更新的數據就是BeanDefinition


 //啟動BeanDefinition的載入

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

        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
        //這里調用容器的refresh,是載入BeanDefinition的入口
            refresh();
        }
    }




這里受到字數限制,emmmmmm所以把剩下的內容放在下一篇文章http://www.lxweimin.com/p/168ae282ebd6里面了,
可以查看下一篇文章進行接下來的內容。

作者:lhsjohn 若要轉載請注明出處

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容