spring源碼學習之路---IOC實現原理(三)


          作者:zuoxiaolong8810(左瀟龍),轉載請注明出處,特別說明:本博文來自博主原博客,為保證新博客中博文的完整性,特復制到此留存,如需轉載請注明新博客地址即可。

          上一章我們已經初步認識了BeanFactory和BeanDefinition,一個是IOC的核心工廠接口,一個是IOC的bean定義接口,上章提到說我們無法讓BeanFactory持有一個Map<String,Object>來完成bean工廠的功能,是因為spring的初始化是可以控制的,可以到用的時候才將bean實例化供開發者使用,除非我們將bean的lazy-init屬性設置為true,初始化bean工廠時采用延遲加載。

          那么知道了上述兩個接口,我相信不少人甚至不看源碼都已經猜到spring是如何做的了。沒錯,就是讓bean工廠持有一個Map<String,BeanDefinition>,這樣就可以在任何時候我們想用哪個bean,取到它的bean定義,我們就可以創造出一個新鮮的實例。

          接口當然不可能持有這樣一個對象,那么這個對象一定是在BeanFactory的某個實現類或者抽象實現類當中所持有的,我經過跋山涉水,終於把它給找出來了,來看DefaultListableBeanFactory。

package org.springframework.beans.factory.support;

import java.io.NotSerializableException;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.inject.Provider;

import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.CannotLoadBeanClassException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.SmartFactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

/**
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @author Costin Leau
 * @since 16 April 2001
 */
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {

    private static Class javaxInjectProviderClass = null;

    static {
        ClassLoader cl = DefaultListableBeanFactory.class.getClassLoader();
        try {
            javaxInjectProviderClass = cl.loadClass("javax.inject.Provider");
        }
        catch (ClassNotFoundException ex) {
            // JSR-330 API not available - Provider interface simply not supported then.
        }
    }


    /** Map from serialized id to factory instance */
    private static final Map<String, Reference<DefaultListableBeanFactory>> serializableFactories =
            new ConcurrentHashMap<String, Reference<DefaultListableBeanFactory>>();

    /** Optional id for this factory, for serialization purposes */
    private String serializationId;

    /** Whether to allow re-registration of a different definition with the same name */
    private boolean allowBeanDefinitionOverriding = true;

    /** Whether to allow eager class loading even for lazy-init beans */
    private boolean allowEagerClassLoading = true;

    /** Resolver to use for checking if a bean definition is an autowire candidate */
    private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver();

    /** Map from dependency type to corresponding autowired value */
    private final Map<Class, Object> resolvableDependencies = new HashMap<Class, Object>();

    /** Map of bean definition objects, keyed by bean name */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

    /** List of bean definition names, in registration order */
    private final List<String> beanDefinitionNames = new ArrayList<String>();

    /** Whether bean definition metadata may be cached for all beans */
    private boolean configurationFrozen = false;

    /** Cached array of bean definition names in case of frozen configuration */
    private String[] frozenBeanDefinitionNames;


}

              注明下,這里我省略了下面N多行源碼,源碼太長,而且太多的話容易混亂,切勿認為此類就這么多了。

              看它名字就知道,這是一個默認的bean工廠實現類,也就是說,如果你需要的功能非常單一,這個實現類已經足夠可以滿足你了,而以后如果你想要對spring的容器擴展,那么只需要擴展或者持有這個對象即可。

        /** Map of bean definition objects, keyed by bean name */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

              看到這一行,其實已經證明了我們的猜測,即使英文不太好,也能看懂它所注釋的意思是bean定義的MAP對象,采用beanName作為key值。

              走到這里,思路已經很明確了,bean工廠的初始化其實就是往這個Map里填充東西。只要把我們XML文件中定義的bean都填充到這里,其實這個工廠就已經可以工作了。

              那么從現在來看,我們需要什么才能把Map填充呢?也就是初始化bean工廠呢,或者說建立IOC容器。我首先列出來以下幾步。

             1.需要一個File指向我們的XML文件(本文的配置文件都已XML為例,因為這是我們最熟悉的),專業點可以叫資源定位,簡單點可以說我們需要一些工具來完成找到XML文件的所在位置。

             2.需要一個Reader來讀取我們的XML文件,專業點叫DOM解析,簡單點說,就是把XML文件的各種定義都給拿出來。

             3.需要將讀出來的數據都設置到Map當中。

             這三部總結起來就是定位、解析、注冊。我們首先按照這個思路來試一下。

             直接上代碼,我們還使用原來的Person類作為一個Bean。

package com.springframework.beans.test;

public class Person {

    public void work(){
        System.out.println("I am working");
    }
}

            我們還需要寫一個簡單的XML文件,beans.xml。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
 <bean id="person" class="com.springframework.beans.test.Person"></bean>
</beans>

           下面是我們根據上述的思路寫一段程序,來看看會發生什么情況。

package com.springframework.beans.test;

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;


public class TestDefaultListableBeanFactory {

    public static void main(String[] args) {
        ClassPathResource classPathResource = new ClassPathResource("beans.xml");
        DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
        xmlBeanDefinitionReader.loadBeanDefinitions(classPathResource);
        System.out.println("numbers: " + defaultListableBeanFactory.getBeanDefinitionCount());
        ((Person)defaultListableBeanFactory.getBean("person")).work();
    }
}

            以下是輸出結果。
 

           可以看到,結果與我們期望的是一樣的,成功的解析了XML文件,並注冊了一個bean定義,而且我們使用getBean方法也成功得到了Person的實例。

           上述這段程序當中可以看出,bean工廠的初始化一共使用了四行程序。

           第一行完成了我們的第一步,即資源定位,采用classpath定位,因為我的beans.xml文件是放在src下面的。

           第二行創建了一個默認的bean工廠。

           第三行創建了一個reader,從名字就不難看出,這個reader是用來讀取XML文件的。這一步要多說一句,其中將我們創建的defaultListableBeanFactory作為參數傳給了reader的構造函數,這里是為了第四步讀取XML文件做准備。

           第四行使用reader解析XML文件,並將讀取的bean定義回調設置到defaultListableBeanFactory當中。其實回調這一步就相當於我們上述的注冊這一步。

           這個時候defaultListableBeanFactory已經被正確初始化了,我們已經可以使用它的一些方法了,比如上面所使用的獲取bean個數,以及獲得一個bean實例的方法。

           但是我相信真正的開發當中,沒有人會采用這樣的方式去創造一個bean工廠,我們可以有更簡單的方式。上面的四步,我們肯定希望一步就可以完成它。是的,這不是在做夢,就像下面這樣。

package com.springframework.beans.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class TestApplicationContext {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new FileSystemXmlApplicationContext("classpath:beans.xml");
        System.out.println("numbers: " + applicationContext.getBeanDefinitionCount());
        ((Person)applicationContext.getBean("person")).work();
    }
}

          接下來就是見證奇跡的時刻了,輸出結果如下圖。        

          我們果然一步就完成了上面四步所做的事情。而且仔細看會發現日志信息當中,第二次采用FileSystemXmlApplicationContext時,日志信息多了許多,分別在上面的前面多了兩行,后面多了兩行,這說明別看我們是一步,但其實這里比上面做了更多的事情。

          具體我們在new一個FileSystemXmlApplicationContext對象的時候,spring到底做了哪些事情呢,這個自然要去跟隨源碼去看個究竟。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM