<input id="ohw05"></input>
  • <table id="ohw05"><menu id="ohw05"></menu></table>
  • <var id="ohw05"></var>
  • <code id="ohw05"><cite id="ohw05"></cite></code>
    <label id="ohw05"></label>
    <var id="ohw05"></var>
  • Spring框架系列(7) - Spring IOC實現原理詳解之IOC初始化流程

    上文,我們看了IOC設計要點和設計結構;緊接著這篇,我們可以看下源碼的實現了:Spring如何實現將資源配置(以xml配置為例)通過加載,解析,生成BeanDefination并注冊到IoC容器中的。@pdai

    引入

    上文,我們看了IOC設計要點和設計結構;緊接著這篇,我們可以看下源碼的實現了:Spring如何實現將資源配置(以xml配置為例)通過加載,解析,生成BeanDefination并注冊到IoC容器中的(就是我們圈出來的部分)

    如何將Bean從XML配置中解析后放到IoC容器中的?

    本文的目標就是分析Spring如何實現將資源配置(以xml配置為例)通過加載,解析,生成BeanDefination并注冊到IoC容器中的。

    初始化的入口

    對于xml配置的Spring應用,在main()方法中實例化ClasspathXmlApplicationContext即可創建一個IoC容器。我們可以從這個構造方法開始,探究一下IoC容器的初始化過程。

     // create and configure beans
    ApplicationContext context = new ClassPathXmlApplicationContext("aspects.xml", "daos.xml", "services.xml");
    
    public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
        this(configLocations, true, (ApplicationContext)null);
    }
    
    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
        // 設置Bean資源加載器
        super(parent);
    
        // 設置配置路徑
        this.setConfigLocations(configLocations);
    
        // 初始化容器
        if (refresh) {
            this.refresh();
        }
    }
    

    設置資源解析器和環境

    調用父類容器AbstractApplicationContext的構造方法(super(parent)方法)為容器設置好Bean資源加載器

    public AbstractApplicationContext(@Nullable ApplicationContext parent) {
        // 默認構造函數初始化容器id, name, 狀態 以及 資源解析器
        this();
    
        // 將父容器的Environment合并到當前容器
        this.setParent(parent);
    }
    

    通過AbstractApplicationContext默認構造函數初始化容器id, name, 狀態 以及 資源解析器

    public AbstractApplicationContext() {
        this.logger = LogFactory.getLog(this.getClass());
        this.id = ObjectUtils.identityToString(this);
        this.displayName = ObjectUtils.identityToString(this);
        this.beanFactoryPostProcessors = new ArrayList();
        this.active = new AtomicBoolean();
        this.closed = new AtomicBoolean();
        this.startupShutdownMonitor = new Object();
        this.applicationStartup = ApplicationStartup.DEFAULT;
        this.applicationListeners = new LinkedHashSet();
        this.resourcePatternResolver = this.getResourcePatternResolver();
    }
    // Spring資源加載器
    protected ResourcePatternResolver getResourcePatternResolver() {
        return new PathMatchingResourcePatternResolver(this);
    }
    

    通過AbstractApplicationContext的setParent(parent)方法將父容器的Environment合并到當前容器

    public void setParent(@Nullable ApplicationContext parent) {
        this.parent = parent;
        if (parent != null) {
            Environment parentEnvironment = parent.getEnvironment();
            if (parentEnvironment instanceof ConfigurableEnvironment) {
                this.getEnvironment().merge((ConfigurableEnvironment)parentEnvironment);
            }
        }
    }
    

    設置配置路徑

    在設置容器的資源加載器之后,接下來FileSystemXmlApplicationContet執行setConfigLocations方法通過調用其父類AbstractRefreshableConfigApplicationContext的方法進行對Bean定義資源文件的定位

    public void setConfigLocations(@Nullable String... locations) {
        if (locations != null) {
            Assert.noNullElements(locations, "Config locations must not be null");
            this.configLocations = new String[locations.length];
    
            for(int i = 0; i < locations.length; ++i) {
                // 解析配置路徑
                this.configLocations[i] = this.resolvePath(locations[i]).trim();
            }
        } else {
            this.configLocations = null;
        }
    }
    protected String resolvePath(String path) {
        // 從上一步Environment中解析
        return this.getEnvironment().resolveRequiredPlaceholders(path);
    }
    

    初始化的主體流程

    Spring IoC容器對Bean定義資源的載入是從refresh()函數開始的,refresh()是一個模板方法,refresh()方法的作用是:在創建IoC容器前,如果已經有容器存在,則需要把已有的容器銷毀和關閉,以保證在refresh之后使用的是新建立起來的IoC容器。refresh的作用類似于對IoC容器的重啟,在新建立好的容器中對容器進行初始化,對Bean定義資源進行載入。

    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
    
            // Prepare this context for refreshing.
            prepareRefresh();
    
            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);
    
            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);
    
                StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);
    
                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);
                beanPostProcess.end();
    
                // Initialize message source for this context.
                initMessageSource();
    
                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();
    
                // Initialize other special beans in specific context subclasses.
                onRefresh();
    
                // Check for listener beans and register them.
                registerListeners();
    
                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);
    
                // Last step: publish corresponding event.
                finishRefresh();
            }
    
            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }
    
                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();
    
                // Reset 'active' flag.
                cancelRefresh(ex);
    
                // Propagate exception to caller.
                throw ex;
            }
    
            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
                contextRefresh.end();
            }
        }
    }
    

    這里的設計上是一個非常典型的資源類加載處理型的思路,頭腦中需要形成如下圖的頂層思路(而不是只停留在流水式的方法上面):

    • 模板方法設計模式,模板方法中使用典型的鉤子方法
    • 具體的初始化加載方法插入到鉤子方法之間
    • 將初始化的階段封裝,用來記錄當前初始化到什么階段;常見的設計是xxxPhase/xxxStage;
    • 資源加載初始化有失敗等處理,必然是try/catch/finally...

    初始化BeanFactory之obtainFreshBeanFactory

    AbstractApplicationContext的obtainFreshBeanFactory()方法調用子類容器的refreshBeanFactory()方法,啟動容器載入Bean定義資源文件的過程,代碼如下:

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        // 這里使用了委派設計模式,父類定義了抽象的refreshBeanFactory()方法,具體實現調用子類容器的refreshBeanFactory()方法
        refreshBeanFactory();
        return getBeanFactory();
    }
    

    AbstractApplicationContext類中只抽象定義了refreshBeanFactory()方法,容器真正調用的是其子類AbstractRefreshableApplicationContext實現的refreshBeanFactory()方法;
    在創建IoC容器前,如果已經有容器存在,則需要把已有的容器銷毀和關閉,以保證在refresh之后使用的是新建立起來的IoC容器。方法的源碼如下:

    protected final void refreshBeanFactory() throws BeansException {
        // 如果已經有容器存在,則需要把已有的容器銷毀和關閉,以保證在refresh之后使用的是新建立起來的IoC容器
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            // 創建DefaultListableBeanFactory,并調用loadBeanDefinitions(beanFactory)裝載bean定義
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory); // 對IoC容器進行定制化,如設置啟動參數,開啟注解的自動裝配等 
            loadBeanDefinitions(beanFactory); // 調用載入Bean定義的方法,主要這里又使用了一個委派模式,在當前類中只定義了抽象的loadBeanDefinitions方法,具體的實現調用子類容器  
            this.beanFactory = beanFactory;
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }
    

    初始化BeanFactory之loadBeanDefinitions

    AbstractRefreshableApplicationContext中只定義了抽象的loadBeanDefinitions方法,容器真正調用的是其子類AbstractXmlApplicationContext對該方法的實現,AbstractXmlApplicationContext的主要源碼如下:

    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // 創建XmlBeanDefinitionReader,即創建Bean讀取器,并通過回調設置到容器中去,容器使用該讀取器讀取Bean定義資源  
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
        // 配置上下文的環境,資源加載器、解析器
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 為Bean讀取器設置SAX xml解析器
    
        // 允許子類自行初始化(比如校驗機制),并提供真正的加載方法
        initBeanDefinitionReader(beanDefinitionReader); // 當Bean讀取器讀取Bean定義的Xml資源文件時,啟用Xml的校驗機制  
        loadBeanDefinitions(beanDefinitionReader);
    }
    
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        // 加載XML配置方式里的Bean定義的資源
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
        // 加載構造函數里配置的Bean配置文件,即{"aspects.xml", "daos.xml", "services.xml"}
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }
    }
    

    Xml Bean讀取器(XmlBeanDefinitionReader)調用其父類AbstractBeanDefinitionReader的 reader.loadBeanDefinitions方法讀取Bean定義資源。

    由于我們使用ClassPathXmlApplicationContext作為例子分析,因此getConfigResources的返回值為null,因此程序執行reader.loadBeanDefinitions(configLocations)分支。

    AbstractBeanDefinitionReader讀取Bean定義資源

    AbstractBeanDefinitionReader的loadBeanDefinitions方法源碼如下:

    @Override
    public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(location, null);
    }
    
    public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
        ResourceLoader resourceLoader = getResourceLoader();
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException(
                    "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
        }
    
        // 模式匹配類型的解析器,這種方式是加載多個滿足匹配條件的資源
        if (resourceLoader instanceof ResourcePatternResolver) {
            try {
                // 獲取到要加載的資源
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                int count = loadBeanDefinitions(resources); // 委派調用其子類XmlBeanDefinitionReader的方法,實現加載功能  
                if (actualResources != null) {
                    Collections.addAll(actualResources, resources);
                }
                if (logger.isTraceEnabled()) {
                    logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
                }
                return count;
            }
            catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "Could not resolve bean definition resource pattern [" + location + "]", ex);
            }
        }
        else {
            // 只能通過絕對路徑URL加載單個資源.
            Resource resource = resourceLoader.getResource(location);
            int count = loadBeanDefinitions(resource);
            if (actualResources != null) {
                actualResources.add(resource);
            }
            if (logger.isTraceEnabled()) {
                logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
            }
            return count;
        }
    }
    
    

    從對AbstractBeanDefinitionReader的loadBeanDefinitions方法源碼分析可以看出該方法做了以下兩件事:

    • 首先,調用資源加載器的獲取資源方法resourceLoader.getResource(location),獲取到要加載的資源。
    • 其次,真正執行加載功能是其子類XmlBeanDefinitionReader的loadBeanDefinitions方法。

    XmlBeanDefinitionReader加載Bean定義資源

    繼續看子類XmlBeanDefinitionReader的loadBeanDefinitions(Resource …)方法看到代表bean文件的資源定義以后的載入過程。

    /**
        * 本質上是加載XML配置的Bean。
        * @param inputSource the SAX InputSource to read from
        * @param resource the resource descriptor for the XML file
        */
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
    
        try {
            Document doc = doLoadDocument(inputSource, resource); // 將Bean定義資源轉換成Document對象
            int count = registerBeanDefinitions(doc, resource);
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + count + " bean definitions from " + resource);
            }
            return count;
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (SAXParseException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
        }
        catch (SAXException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "XML document from " + resource + " is invalid", ex);
        }
        catch (ParserConfigurationException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Parser configuration exception parsing XML from " + resource, ex);
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "IOException parsing XML document from " + resource, ex);
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Unexpected exception parsing XML document from " + resource, ex);
        }
    }
    
    // 使用配置的DocumentLoader加載XML定義文件為Document.
    protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                getValidationModeForResource(resource), isNamespaceAware());
    }
    

    通過源碼分析,載入Bean定義資源文件的最后一步是將Bean定義資源轉換為Document對象,該過程由documentLoader實現

    DocumentLoader將Bean定義資源轉換為Document對象

    DocumentLoader將Bean定義資源轉換成Document對象的源碼如下:

    // 使用標準的JAXP將載入的Bean定義資源轉換成document對象
    @Override
    public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
            ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
    
        // 創建文件解析器工廠
        DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
        if (logger.isTraceEnabled()) {
            logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
        }
        // 創建文檔解析器
        DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
        return builder.parse(inputSource); // 解析
    }
    
    protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
            throws ParserConfigurationException {
    
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(namespaceAware);
    
        // 設置解析XML的校驗
        if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
            factory.setValidating(true);
            if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
                // Enforce namespace aware for XSD...
                factory.setNamespaceAware(true);
                try {
                    factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
                }
                catch (IllegalArgumentException ex) {
                    ParserConfigurationException pcex = new ParserConfigurationException(
                            "Unable to validate using XSD: Your JAXP provider [" + factory +
                            "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
                            "Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
                    pcex.initCause(ex);
                    throw pcex;
                }
            }
        }
    
        return factory;
    }
    

    該解析過程調用JavaEE標準的JAXP標準進行處理。

    至此Spring IoC容器根據定位的Bean定義資源文件,將其加載讀入并轉換成為Document對象過程完成。

    接下來我們要繼續分析Spring IoC容器將載入的Bean定義資源文件轉換為Document對象之后,是如何將其解析為Spring IoC管理的Bean對象并將其注冊到容器中的。

    XmlBeanDefinitionReader解析載入的Bean定義資源文件

    XmlBeanDefinitionReader類中的doLoadBeanDefinitions方法是從特定XML文件中實際載入Bean定義資源的方法,該方法在載入Bean定義資源之后將其轉換為Document對象,接下來調用registerBeanDefinitions啟動Spring IoC容器對Bean定義的解析過程,registerBeanDefinitions方法源碼如下:

    // 按照Spring的Bean語義要求將Bean定義資源解析并轉換為容器內部數據結構 
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();
        // 解析過程入口,這里使用了委派模式,具體的解析實現過程有實現類DefaultBeanDefinitionDocumentReader完成  
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;  // 返回此次解析了多少個對象
    }
    
    // 創建BeanDefinitionDocumentReader對象,解析Document對象  
    protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
        return BeanUtils.instantiateClass(this.documentReaderClass);
    }
    
    /**
        * Create the {@link XmlReaderContext} to pass over to the document reader.
        */
    public XmlReaderContext createReaderContext(Resource resource) {
        return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
                this.sourceExtractor, this, getNamespaceHandlerResolver());
    }
    

    Bean定義資源的載入解析分為以下兩個過程:

    • 首先,通過調用XML解析器將Bean定義資源文件轉換得到Document對象,但是這些Document對象并沒有按照Spring的Bean規則進行解析。這一步是載入的過程
    • 其次,在完成通用的XML解析之后,按照Spring的Bean規則對Document對象進行解析。

    按照Spring的Bean規則對Document對象解析的過程是在接口BeanDefinitionDocumentReader的實現類DefaultBeanDefinitionDocumentReader中實現的。

    DefaultBeanDefinitionDocumentReader對Bean定義的Document對象解析

    BeanDefinitionDocumentReader接口通過registerBeanDefinitions方法調用其實現類DefaultBeanDefinitionDocumentReader對Document對象進行解析,解析的代碼如下:

    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        doRegisterBeanDefinitions(doc.getDocumentElement());
    }
    
    // 注冊<beans/>配置的Beans
    @SuppressWarnings("deprecation")  // for Environment.acceptsProfiles(String...)
    protected void doRegisterBeanDefinitions(Element root) {
        // Any nested <beans> elements will cause recursion in this method. In
        // order to propagate and preserve <beans> default-* attributes correctly,
        // keep track of the current (parent) delegate, which may be null. Create
        // the new (child) delegate with a reference to the parent for fallback purposes,
        // then ultimately reset this.delegate back to its original (parent) reference.
        // this behavior emulates a stack of delegates without actually necessitating one.
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);
    
        if (this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                // We cannot use Profiles.of(...) since profile expressions are not supported
                // in XML config. See SPR-12458 for details.
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }
    
        preProcessXml(root);
        parseBeanDefinitions(root, this.delegate); // 從Document的根元素開始進行Bean定義的Document對象  
        postProcessXml(root);
    
        this.delegate = parent;
    }
    

    BeanDefinitionParserDelegate解析Bean定義資源文件生成BeanDefinition

    /**
        * Parse the elements at the root level in the document:
        * "import", "alias", "bean".
        * @param root the DOM root element of the document
        */
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    if (delegate.isDefaultNamespace(ele)) {
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }
    
    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
          
        // 如果元素節點是<Import>導入元素,進行導入解析
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        // 如果元素節點是<Alias>別名元素,進行別名解析 
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        // 如果元素節點<Bean>元素, 按照Spring的Bean規則解析元素  
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        // 如果元素節點<Beans>元素,即它是嵌套類型的
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // 遞歸解析
            doRegisterBeanDefinitions(ele);
        }
    }
    

    解析Bean生成BeanDefinitionHolder的方法

    /**
        * Process the given bean element, parsing the bean definition
        * and registering it with the registry.
        */
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // 注冊最終的裝飾實例
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }
    

    (這里就不展開了,無非就是解析XML各種元素,來生成BeanDefinition)

    解析過后的BeanDefinition在IoC容器中的注冊

    Document對象的解析后得到封裝BeanDefinition的BeanDefinitionHold對象,然后調用BeanDefinitionReaderUtils的registerBeanDefinition方法向IoC容器注冊解析的Bean,BeanDefinitionReaderUtils的注冊的源碼如下:

    // 通過BeanDefinitionRegistry將BeanDefinitionHolder注冊到BeanFactory
    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {
    
        // Register bean definition under primary name.
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    
        // Register aliases for bean name, if any.
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }
    

    當調用BeanDefinitionReaderUtils向IoC容器注冊解析的BeanDefinition時,真正完成注冊功能的是DefaultListableBeanFactory。

    DefaultListableBeanFactory向IoC容器注冊解析后的BeanDefinition

    IOC容器本質上就是一個beanDefinitionMap, 注冊即將BeanDefinition put到map中

    /** Map of bean definition objects, keyed by bean name. */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    
    /** Map from bean name to merged BeanDefinitionHolder. */
    private final Map<String, BeanDefinitionHolder> mergedBeanDefinitionHolders = new ConcurrentHashMap<>(256);
    
    
    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {
    
        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    
        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }
    
        BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
        // 如果已經注冊
        if (existingDefinition != null) {
            // 檢查是否可以覆蓋
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
            }
            else if (existingDefinition.getRole() < beanDefinition.getRole()) {
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                if (logger.isInfoEnabled()) {
                    logger.info("Overriding user-defined bean definition for bean '" + beanName +
                            "' with a framework-generated bean definition: replacing [" +
                            existingDefinition + "] with [" + beanDefinition + "]");
                }
            }
            else if (!beanDefinition.equals(existingDefinition)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Overriding bean definition for bean '" + beanName +
                            "' with a different definition: replacing [" + existingDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            else {
                if (logger.isTraceEnabled()) {
                    logger.trace("Overriding bean definition for bean '" + beanName +
                            "' with an equivalent definition: replacing [" + existingDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            // 覆蓋
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
                synchronized (this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    removeManualSingletonName(beanName);
                }
            }
            else {
                // Still in startup registration phase
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                removeManualSingletonName(beanName);
            }
            //重置所有已經注冊過的BeanDefinition的緩存  
            this.frozenBeanDefinitionNames = null;
        }
    
        if (existingDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
        }
        else if (isConfigurationFrozen()) {
            clearByTypeCache();
        }
    }
    

    至此,Bean定義資源文件中配置的Bean被解析過后,已經注冊到IoC容器中,被容器管理起來,真正完成了IoC容器初始化所做的全部工作。現 在IoC容器中已經建立了整個Bean的配置信息,這些BeanDefinition信息已經可以使用,并且可以被檢索,IoC容器的作用就是對這些注冊的Bean定義信息進行處理和維護。這些的注冊的Bean定義信息是IoC容器控制反轉的基礎,正是有了這些注冊的數據,容器才可以進行依賴注入。

    總結

    現在通過上面的代碼,總結一下IOC容器初始化的基本步驟:

    • 初始化的入口在容器實現中的 refresh()調用來完成

    • 對 bean 定義載入 IOC 容器使用的方法是 loadBeanDefinition,其中的大致過程如下:

      • 通過 ResourceLoader 來完成資源文件位置的定位,DefaultResourceLoader 是默認的實現,同時上下文本身就給出了 ResourceLoader 的實現,可以從類路徑,文件系統, URL 等方式來定為資源位置。如果是 XmlBeanFactory作為 IOC 容器,那么需要為它指定 bean 定義的資源,也就是說 bean 定義文件時通過抽象成 Resource 來被 IOC 容器處理的
      • 通過 BeanDefinitionReader來完成定義信息的解析和 Bean 信息的注冊, 往往使用的是XmlBeanDefinitionReader 來解析 bean 的 xml 定義文件 - 實際的處理過程是委托給 BeanDefinitionParserDelegate 來完成的,從而得到 bean 的定義信息,這些信息在 Spring 中使用 BeanDefinition 對象來表示 - 這個名字可以讓我們想到loadBeanDefinition,RegisterBeanDefinition 這些相關的方法 - 他們都是為處理 BeanDefinitin 服務的
      • 容器解析得到 BeanDefinition 以后,需要把它在 IOC 容器中注冊,這由 IOC 實現 BeanDefinitionRegistry 接口來實現。注冊過程就是在 IOC 容器內部維護的一個HashMap 來保存得到的 BeanDefinition 的過程。這個 HashMap 是 IoC 容器持有 bean 信息的場所,以后對 bean 的操作都是圍繞這個HashMap 來實現的.
    • 然后我們就可以通過 BeanFactory 和 ApplicationContext 來享受到 Spring IOC 的服務了,在使用 IOC 容器的時候,我們注意到除了少量粘合代碼,絕大多數以正確 IoC 風格編寫的應用程序代碼完全不用關心如何到達工廠,因為容器將把這些對象與容器管理的其他對象鉤在一起。基本的策略是把工廠放到已知的地方,最好是放在對預期使用的上下文有意義的地方,以及代碼將實際需要訪問工廠的地方。 Spring 本身提供了對聲明式載入 web 應用程序用法的應用程序上下文,并將其存儲在ServletContext 中的框架實現。

    參考文章

    https://blog.csdn.net/qq_36212439/article/details/82749963

    https://juejin.cn/post/6973884466171215908

    https://juejin.cn/post/6844903838743265294

    https://blog.csdn.net/hjing123/article/details/104867343

    http://www.tnepal.com/wl20200316/p/12522993.html

    更多文章

    首先, 從Spring框架的整體架構和組成對整體框架有個認知。

    • Spring基礎 - Spring和Spring框架組成
      • Spring是什么?它是怎么誕生的?有哪些主要的組件和核心功能呢? 本文通過這幾個問題幫助你構筑Spring和Spring Framework的整體認知。

    其次,通過案例引出Spring的核心(IoC和AOP),同時對IoC和AOP進行案例使用分析。

    基于Spring框架和IOC,AOP的基礎,為構建上層web應用,需要進一步學習SpringMVC。

    • Spring基礎 - SpringMVC請求流程和案例
      • 前文我們介紹了Spring框架和Spring框架中最為重要的兩個技術點(IOC和AOP),那我們如何更好的構建上層的應用呢(比如web 應用),這便是SpringMVC;Spring MVC是Spring在Spring Container Core和AOP等技術基礎上,遵循上述Web MVC的規范推出的web開發框架,目的是為了簡化Java棧的web開發。 本文主要介紹SpringMVC的請求流程和基礎案例的編寫和運行。

    Spring進階 - IoC,AOP以及SpringMVC的源碼分析

    • Spring進階 - Spring IOC實現原理詳解之IOC體系結構設計
      • 在對IoC有了初步的認知后,我們開始對IOC的實現原理進行深入理解。本文將幫助你站在設計者的角度去看IOC最頂層的結構設計
    • Spring進階 - Spring IOC實現原理詳解之IOC初始化流程
      • 上文,我們看了IOC設計要點和設計結構;緊接著這篇,我們可以看下源碼的實現了:Spring如何實現將資源配置(以xml配置為例)通過加載,解析,生成BeanDefination并注冊到IoC容器中的
    • Spring進階 - Spring IOC實現原理詳解之Bean實例化(生命周期,循環依賴等)
      • 上文,我們看了IOC設計要點和設計結構;以及Spring如何實現將資源配置(以xml配置為例)通過加載,解析,生成BeanDefination并注冊到IoC容器中的;容器中存放的是Bean的定義即BeanDefinition放到beanDefinitionMap中,本質上是一個ConcurrentHashMap<String, Object>;并且BeanDefinition接口中包含了這個類的Class信息以及是否是單例等。那么如何從BeanDefinition中實例化Bean對象呢,這是本文主要研究的內容?
    • Spring進階 - Spring AOP實現原理詳解之切面實現
      • 前文,我們分析了Spring IOC的初始化過程和Bean的生命周期等,而Spring AOP也是基于IOC的Bean加載來實現的。本文主要介紹Spring AOP原理解析的切面實現過程(將切面類的所有切面方法根據使用的注解生成對應Advice,并將Advice連同切入點匹配器和切面類等信息一并封裝到Advisor,為后續交給代理增強實現做準備的過程)。
    • Spring進階 - Spring AOP實現原理詳解之AOP代理
      • 上文我們介紹了Spring AOP原理解析的切面實現過程(將切面類的所有切面方法根據使用的注解生成對應Advice,并將Advice連同切入點匹配器和切面類等信息一并封裝到Advisor)。本文在此基礎上繼續介紹,代理(cglib代理和JDK代理)的實現過程。
    • Spring進階 - Spring AOP實現原理詳解之Cglib代理實現
      • 我們在前文中已經介紹了SpringAOP的切面實現和創建動態代理的過程,那么動態代理是如何工作的呢?本文主要介紹Cglib動態代理的案例和SpringAOP實現的原理。
    • Spring進階 - Spring AOP實現原理詳解之JDK代理實現
      • 上文我們學習了SpringAOP Cglib動態代理的實現,本文主要是SpringAOP JDK動態代理的案例和實現部分。
    • Spring進階 - SpringMVC實現原理之DispatcherServlet初始化的過程
      • 前文我們有了IOC的源碼基礎以及SpringMVC的基礎,我們便可以進一步深入理解SpringMVC主要實現原理,包含DispatcherServlet的初始化過程和DispatcherServlet處理請求的過程的源碼解析。本文是第一篇:DispatcherServlet的初始化過程的源碼解析。
    • Spring進階 - SpringMVC實現原理之DispatcherServlet處理請求的過程
      • 前文我們有了IOC的源碼基礎以及SpringMVC的基礎,我們便可以進一步深入理解SpringMVC主要實現原理,包含DispatcherServlet的初始化過程和DispatcherServlet處理請求的過程的源碼解析。本文是第二篇:DispatcherServlet處理請求的過程的源碼解析。
    posted @ 2022-06-28 08:37  pdai  閱讀(229)  評論(0編輯  收藏  舉報
    国产美女a做受大片观看