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

SpringFrameWork的核心是IOC容器的實現(xiàn)

1. IOC容器和依賴反轉(zhuǎn)模式

依賴反轉(zhuǎn)模式是為了解耦對象之間的依賴關(guān)系而誕生的一種模式或者說是一種思想,矯情來說,你甚至可以把它當(dāng)成是一種情懷汉矿。

傳統(tǒng)來說,合作對象的引用以及依賴關(guān)系由具體對象來完成,會造成高度耦合和代碼可測試性降低,而在面向?qū)ο蟮南到y(tǒng)中,對象封裝了數(shù)據(jù)和對數(shù)據(jù)的處理,對象的依賴關(guān)系常常體現(xiàn)在對數(shù)據(jù)和方法的依賴上.而這個時候如果將這些對象的依賴注入交給框架或者IOC容器來完成,就相當(dāng)于將注入依賴的責(zé)任反轉(zhuǎn)交給了外界,而不再是由具體對象去操心這些事情了.

1.1 依賴控制反轉(zhuǎn)的實現(xiàn)方式

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

2.1 Spring的IOC容器系列

BeanFactory和ApplicationContext都可以看作是容器的具體表現(xiàn)形式绪爸。

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

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

Spring的ioc容器系列.png

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

BeanDefinition是我們對Bean定義的一種抽象,是對依賴反轉(zhuǎn)模式中管的對象依賴關(guān)系的一種抽象,是容器實現(xiàn)依賴反轉(zhuǎn)功能的核心數(shù)據(jù)結(jié)構(gòu)。

2. IOC容器的實現(xiàn):BeanFactory和ApplicationContext

2.21 BeanFactory對Ioc容器的功能定義

正如我們上面所說,BeanFactory定義了容器功能的基本規(guī)范,這個基本規(guī)范中包括了諸如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的實現(xiàn)原理

首先讓我們看一下XmlBeanFactory的繼承體系結(jié)構(gòu)

XmlBeanFactory的繼承結(jié)構(gòu).png

BeanFactory接口提供了使用ioc容器的規(guī)范,在這個基礎(chǔ)上,Spring提供了符合這個ioc容器接口的一系列容器的實現(xiàn)供開發(fā)人員使用。

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

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

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

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

我們可以看一下XmlBeanFactory的代碼實現(xiàn)

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代碼的實現(xiàn),我們用編程的方式來使用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.創(chuàng)建ioc配置文件的抽象資源,該抽象資源包含了BeanDefinition的定義信息
2.創(chuàng)建一個BeanFactory,這里使用DefaultListableBeanFactory
3.創(chuàng)建一個載入BeanDefinition的讀取器,這里使用XmlBeanDefinitionReader來載入XML文件形式的BeanDefinition 通過一個回調(diào)配置給BeanFactory
4.從定義好的資源位置讀取配置信息,具體的解析過程由XmlBeanDefinitionReader來完成椿疗。完成整個載入和注冊的Bean定義之后,需要的ioc容器就建立起來了漏峰,這個時候IOC容器就可以使用了.

2.2.3 ApplicationContext的特點

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

ApplicationContext的接口關(guān)系.png

1.支持不同的信息源

ApplicationContext擴展了MessageSource接口,這些信息源的擴展可以支持國際化的實現(xiàn),為開發(fā)多語言版本的應(yīng)用提供服務(wù).

2.訪問資源

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

3.支持應(yīng)用事件

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

2.3 IOC容器的初始化

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

2.3.1 BeanDefinition的Resource定位

常見的相關(guān)的類有FileSystemXmlApplicationContext ...ClassPathXmlApplicationContext....以及XmlWebApplicationContext等。

FileSystemXmlApplicationContext : 從文件系統(tǒng)載入Resource
ClassPathXmlApplicationContext: 從Class Path載入Resource
XmlWebApplicationContext: 在Web容器中載入Resource

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

FileSystemXmlApplicationContext的繼承體系.png

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

下面我們來看FileSystemXmlApplicationContext的實現(xiàn)

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
     */
     //這個構(gòu)造函數(shù)的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
     */
     //這個構(gòu)造函數(shù)允許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
     */
    //這個構(gòu)造函數(shù)在允許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()
     */
     //在對象的初始化過程中,調(diào)用refresh函數(shù)載入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
     */
     //這是應(yīng)用于文件系統(tǒng)的Resource的實現(xiàn),通過構(gòu)造一個FileSystemResource來得到一個在文件系統(tǒng)定位的BeanDefinition
     //這個getResourceByPath是在BeanDefinitionReader的loadBeanDefinition中被調(diào)用的,采用了魔板模式,具體的定為實現(xiàn)由子類來完成.
     
    
    @Override
    protected Resource getResourceByPath(String path) {
        if (path != null && path.startsWith("/")) {
            path = path.substring(1);
        }
        return new FileSystemResource(path);
    }

}

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

在這里我門最需要明白的一點是,在構(gòu)造函數(shù)中通過refresh來啟動了容器的初始化贤壁。

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

getResourceByPath的調(diào)用關(guān)系.png

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

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


    @Override
    protected final void refreshBeanFactory() throws BeansException {
    //這里是判斷,如果已經(jīng)建立了BeanFactory,則銷毀并關(guān)閉該BeanFactory
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
        //這里是創(chuàng)建并設(shè)置持有DefaultListableBeanFactory的地方
        //同時調(diào)用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);
        }
    }

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

    protected DefaultListableBeanFactory createBeanFactory() {
        return new DefaultListableBeanFactory(getInternalParentBeanFactory());
    }
 
   //這里是使用BeanDefinitionReader載入Bean定義的地方,因為允許有許多種載入方式,這里通過一個抽象函數(shù)把具體的實現(xiàn)委托給子類完成
    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 {
            //調(diào)用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.
          //調(diào)用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標(biāo)識的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標(biāo)識的Resource定位
                URL url = new URL(location);
                return new UrlResource(url);
            }
            catch (MalformedURLException ex) {
                // No URL -> resolve as resource path.
                //如果既不是classpath也不是url標(biāo)識的Resource定位,則把getResource的重任交給getResourceByPath
                //這個方法是一個protected方法,默認(rèn)的實現(xiàn)是得到一個ClassPathContextResource,這個方法常常會用子類來實現(xiàn).
                return getResourceByPath(location);
            }
        }
 

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

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

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

Resource的定義和繼承關(guān)系.png

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

2.3.2 BeanDefinition的載入和解析

對于ioc容器來說,BeanDefinition的載入過程相當(dāng)于把我們定義的BeaDefinition在ioc容器中轉(zhuǎn)化成一個Spring內(nèi)部表示的數(shù)據(jù)結(jié)構(gòu)的過程.
IOC容器對Bean的管理和依賴注入功能的實現(xiàn),是通過對其持有的BeanDefinition進行各種相關(guān)的操作來完成的.這些BeanDefinition數(shù)據(jù)在ioc容器里面通過一個HashMap來保持和維護.

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

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


 //啟動BeanDefinition的載入

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

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




這里受到字?jǐn)?shù)限制,emmmmmm所以把剩下的內(nèi)容放在下一篇文章http://www.reibang.com/p/168ae282ebd6里面了,
可以查看下一篇文章進行接下來的內(nèi)容给梅。

作者:lhsjohn 若要轉(zhuǎn)載請注明出處

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末假丧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子动羽,更是在濱河造成了極大的恐慌包帚,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件运吓,死亡現(xiàn)場離奇詭異渴邦,居然都是意外死亡,警方通過查閱死者的電腦和手機拘哨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門谋梭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人倦青,你說我怎么就攤上這事瓮床。” “怎么了产镐?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵隘庄,是天一觀的道長。 經(jīng)常有香客問我癣亚,道長丑掺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任述雾,我火速辦了婚禮街州,結(jié)果婚禮上兼丰,老公的妹妹穿的比我還像新娘。我一直安慰自己唆缴,他們只是感情好鳍征,可當(dāng)我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著琐谤,像睡著了一般蟆技。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上斗忌,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天质礼,我揣著相機與錄音,去河邊找鬼织阳。 笑死眶蕉,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的唧躲。 我是一名探鬼主播造挽,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼弄痹!你這毒婦竟也來了饭入?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤肛真,失蹤者是張志新(化名)和其女友劉穎谐丢,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蚓让,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡乾忱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了历极。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窄瘟。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖趟卸,靈堂內(nèi)的尸體忽然破棺而出蹄葱,到底是詐尸還是另有隱情,我是刑警寧澤锄列,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布新蟆,位于F島的核電站,受9級特大地震影響右蕊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吮螺,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一饶囚、第九天 我趴在偏房一處隱蔽的房頂上張望帕翻。 院中可真熱鬧,春花似錦萝风、人聲如沸嘀掸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽睬塌。三九已至,卻和暖如春歇万,著一層夾襖步出監(jiān)牢的瞬間揩晴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工贪磺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留硫兰,地道東北人。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓寒锚,卻偏偏與公主長得像劫映,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子刹前,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容