成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Spring-IOC容器容器

BigTomato / 1761人閱讀

摘要:使用別名時(shí),容器首先將別名元素所定義的別名注冊(cè)到容器中。調(diào)用的方法向容器注冊(cè)解析的通過(guò)對(duì)對(duì)象的解析和封裝返回一個(gè)通過(guò)這個(gè)來(lái)注冊(cè)對(duì)象當(dāng)調(diào)用向容器注冊(cè)解析的時(shí),真正完成注冊(cè)功能的是。

文章參考來(lái)自:https://www.cnblogs.com/ITtan...
文章代碼來(lái)自 spring-boot 1.4.1 Release版本

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

public void refresh() throws BeansException, IllegalStateException {
       Object var1 = this.startupShutdownMonitor;
       synchronized(this.startupShutdownMonitor) {
           //調(diào)用容器準(zhǔn)備刷新的方法,獲取容器的當(dāng)時(shí)時(shí)間,同時(shí)給容器設(shè)置同步標(biāo)識(shí)
           this.prepareRefresh();
           //告訴子類啟動(dòng)refreshBeanFactory()方法,Bean定義資源文件的載入從  
           //子類的refreshBeanFactory()方法啟動(dòng) 
           //這個(gè)bean的載入過(guò)程 包括對(duì)xml的解析和加載為BeanDefinitions 都是從this.obtainFreshBeanFactory()這里進(jìn)入
           ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
           /*這之后的代碼都是注冊(cè)容器的信息源和生命周期事件*/
           //為BeanFactory配置容器特性,例如類加載器、事件處理器等
           this.prepareBeanFactory(beanFactory);

           try {
               //為容器的某些子類指定特殊的BeanPost事件處理器
               this.postProcessBeanFactory(beanFactory);
               //調(diào)用所有注冊(cè)的BeanFactoryPostProcessor的Bean
               this.invokeBeanFactoryPostProcessors(beanFactory);
               //為BeanFactory注冊(cè)BeanPost事件處理器.  
               //BeanPostProcessor是Bean后置處理器,用于監(jiān)聽容器觸發(fā)的事件 
               this.registerBeanPostProcessors(beanFactory);
               //初始化信息源,和國(guó)際化相關(guān).
               this.initMessageSource();
               //初始化容器事件傳播器.
               this.initApplicationEventMulticaster();
               //調(diào)用子類的某些特殊Bean初始化方法
               this.onRefresh();
               //為事件傳播器注冊(cè)事件監(jiān)聽器. 
               this.registerListeners();
               //初始化所有剩余的單態(tài)Bean.
               this.finishBeanFactoryInitialization(beanFactory);
               //初始化容器的生命周期事件處理器,并發(fā)布容器的生命周期事件
               this.finishRefresh();
           } catch (BeansException var9) {
               if (this.logger.isWarnEnabled()) {
                   this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
               }
               //銷毀以創(chuàng)建的單態(tài)Bean
               this.destroyBeans();
               //取消refresh操作,重置容器的同步標(biāo)識(shí).
               this.cancelRefresh(var9);
               throw var9;
           } finally {
               this.resetCommonCaches();
           }

       }
   }

AbstractRefreshableApplicationContext 類:
AbstractRefreshableApplicationContext中只定義了抽象的loadBeanDefinitions方法,容器真正調(diào)用的是其子類AbstractXmlApplicationContext對(duì)該方法的實(shí)現(xiàn)

protected final void refreshBeanFactory() throws BeansException {
        //如果已經(jīng)創(chuàng)建了BeanFactory,則銷毀并關(guān)閉BeanFactory
        if (this.hasBeanFactory()) {
            this.destroyBeans();
            this.closeBeanFactory();
        }

        try {
             //創(chuàng)建了一個(gè)IOC容器
            DefaultListableBeanFactory beanFactory = this.createBeanFactory();
            beanFactory.setSerializationId(this.getId());
            /對(duì)IoC容器進(jìn)行定制化,如設(shè)置啟動(dòng)參數(shù),開啟注解的自動(dòng)裝配等
            this.customizeBeanFactory(beanFactory);
            //調(diào)用載入Bean定義的方法,主要這里又使用了一個(gè)委派模式,在當(dāng)前類中只定義了抽象的loadBeanDefinitions方法,具體的實(shí)現(xiàn)調(diào)用子類容器
            this.loadBeanDefinitions(beanFactory);
            Object var2 = this.beanFactoryMonitor;
            synchronized(this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        } catch (IOException var5) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
        }
    }

AbstractXmlApplicationContext 類:

 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        //創(chuàng)建XmlBeanDefinitionReader,即創(chuàng)建Bean讀取器,并通過(guò)回調(diào)設(shè)置到容器中去,容器使用該讀取器讀取Bean定義資源  
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        beanDefinitionReader.setEnvironment(this.getEnvironment());
         //為Bean讀取器設(shè)置Spring資源加載器,AbstractXmlApplicationContext的  
        //祖先父類AbstractApplicationContext繼承DefaultResourceLoader,因此,容器本身也是一個(gè)資源加載器
        beanDefinitionReader.setResourceLoader(this);
        //為Bean讀取器設(shè)置SAX xml解析器 
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        //當(dāng)Bean讀取器讀取Bean定義的Xml資源文件時(shí),啟用Xml的校驗(yàn)機(jī)制  
        this.initBeanDefinitionReader(beanDefinitionReader);
        //Bean讀取器真正實(shí)現(xiàn)加載的方法
        this.loadBeanDefinitions(beanDefinitionReader);
    }
    
    protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
        reader.setValidating(this.validating);
    }

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = this.getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }

        String[] configLocations = this.getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }

    }

    @Nullable
    protected Resource[] getConfigResources() {
        return null;
    }

在其抽象父類AbstractBeanDefinitionReader中定義了載入過(guò)程

 public int loadBeanDefinitions(String location, @Nullable Set actualResources) throws BeanDefinitionStoreException {
        //獲取resourceLoader
        ResourceLoader resourceLoader = this.getResourceLoader();
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        } else {
            int loadCount;
            if (!(resourceLoader instanceof ResourcePatternResolver)) {
                //將指定位置的Bean定義資源文件解析為Spring IoC容器封裝的資源  
                //加載多個(gè)指定位置的Bean定義資源文件 完成具體的資源定位的工作               
                Resource resource = resourceLoader.getResource(location);
                //加載資源 開始我們的第二步操作 轉(zhuǎn)換為BeanDefinition對(duì)象
                loadCount = this.loadBeanDefinitions((Resource)resource);
                if (actualResources != null) {
                    actualResources.add(resource);
                }

                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
                }

                return loadCount;
            } else {
                try {
                   //將指定位置的Bean定義資源文件解析為Spring IoC容器封裝的資源  
                   //加載單個(gè)指定位置的Bean定義資源文件  
                    Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
                    //加載資源 開始我們的第二步操作 轉(zhuǎn)換為BeanDefinition對(duì)象
                    loadCount = this.loadBeanDefinitions(resources);
                    if (actualResources != null) {
                        Resource[] var6 = resources;
                        int var7 = resources.length;

                        for(int var8 = 0; var8 < var7; ++var8) {
                            Resource resource = var6[var8];
                            actualResources.add(resource);
                        }
                    }

                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                    }

                    return loadCount;
                } catch (IOException var10) {
                    throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", var10);
                }
            }
        }
    }

具體加載資源的方法
首先,調(diào)用資源加載器的獲取資源方法resourceLoader.getResource(location),獲取到要加載的資源。
其次,真正執(zhí)行加載功能是其子類XmlBeanDefinitionReader的loadBeanDefinitions方法。
資源定位到這里就結(jié)束 最終返回的是一個(gè)Resource的對(duì)象來(lái)進(jìn)行BeanDefinition的載入。在定位完成后,為BeanDefinition的載入創(chuàng)客I/O條件,
但是具體的載入還沒有開始載入。

public Resource getResource(String location) {
        Assert.notNull(location, "Location must not be null");
        Iterator var2 = this.protocolResolvers.iterator();

        Resource resource;
        do {
            if (!var2.hasNext()) {
               //處理/開頭的定位
                if (location.startsWith("/")) {
                    return this.getResourceByPath(location);
                }
                //帶有classpath標(biāo)識(shí)的Resource
                if (location.startsWith("classpath:")) {
                    return new ClassPathResource(location.substring("classpath:".length()), this.getClassLoader());
                }

                try {
                //處理URL標(biāo)識(shí)的Resource定位
                    URL url = new URL(location);
                    return (Resource)(ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
                } catch (MalformedURLException var5) {
                    //如果不存classpath、Url、/標(biāo)志的 就教給他處理
                    return this.getResourceByPath(location);
                }
            }

            ProtocolResolver protocolResolver = (ProtocolResolver)var2.next();
            resource = protocolResolver.resolve(location, this);
        } while(resource == null);

        return resource;
    }
protected Resource getResourceByPath(String path) {    
   if (path != null && path.startsWith("/")) {    
        path = path.substring(1);    
    }  
    //這里使用文件系統(tǒng)資源對(duì)象來(lái)定義bean 文件
    return new FileSystemResource(path);  
}

BeanDefinition的載入分成兩部分,首先通過(guò)XML的解析器得到document對(duì)象,
但這些document對(duì)象沒有按照spring的Bean規(guī)則進(jìn)行解析。在完成XML解析后,才是按照Spring的Bean規(guī)則進(jìn)行解析,這個(gè)解析的過(guò)程是在documentReader實(shí)現(xiàn)。

 public int loadBeanDefinitions(String location, @Nullable Set actualResources) throws BeanDefinitionStoreException {
        //獲取resourceLoader
        ResourceLoader resourceLoader = this.getResourceLoader();
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        } else {
            int loadCount;
            if (!(resourceLoader instanceof ResourcePatternResolver)) {
                //將指定位置的Bean定義資源文件解析為Spring IoC容器封裝的資源  
                //加載多個(gè)指定位置的Bean定義資源文件 完成具體的資源定位的工作               
                Resource resource = resourceLoader.getResource(location);
 
                //加載資源 開始我們的第二步操作 轉(zhuǎn)換為BeanDefinition對(duì)象
                loadCount = this.loadBeanDefinitions((Resource)resource);
                if (actualResources != null) {
                    actualResources.add(resource);
                }

                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
                }

                return loadCount;
            } else {
                try {
                   //將指定位置的Bean定義資源文件解析為Spring IoC容器封裝的資源  
                   //加載單個(gè)指定位置的Bean定義資源文件  
                    Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
                    //加載資源 開始我們的第二步操作 轉(zhuǎn)換為BeanDefinition對(duì)象
                    loadCount = this.loadBeanDefinitions(resources);
                    if (actualResources != null) {
                        Resource[] var6 = resources;
                        int var7 = resources.length;

                        for(int var8 = 0; var8 < var7; ++var8) {
                            Resource resource = var6[var8];
                            actualResources.add(resource);
                        }
                    }

                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                    }

                    return loadCount;
                } catch (IOException var10) {
                    throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", var10);
                }
            }
        }
    }

xml文件轉(zhuǎn)換為document對(duì)象 再解析BeanDefinition
這里是載入XML形式Bean定義資源文件方法

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if(this.logger.isInfoEnabled()) {
            this.logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }

        Set currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
        if(currentResources == null) {
            currentResources = new HashSet(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }

        if(!((Set)currentResources).add(encodedResource)) {
            throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        } else {
            int var5;
            try {
                //將資源文件轉(zhuǎn)為InputStream的IO流
                InputStream inputStream = encodedResource.getResource().getInputStream();

                try {
                    //從InputStream中得到XML的解析源
                    InputSource inputSource = new InputSource(inputStream);
                    if(encodedResource.getEncoding() != null) {
                        inputSource.setEncoding(encodedResource.getEncoding());
                    }
                     //這里是具體的讀取過(guò)程
                    var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
                } finally {
                    //IO流的關(guān)閉
                    inputStream.close();
                }
            } catch (IOException var15) {
                throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
            } finally {
                ((Set)currentResources).remove(encodedResource);
                if(((Set)currentResources).isEmpty()) {
                    this.resourcesCurrentlyBeingLoaded.remove();
                }

            }

            return var5;
        }
    }
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
        try {
            //@1將XML文件轉(zhuǎn)換為DOM對(duì)象,解析過(guò)程由documentLoader實(shí)現(xiàn)
            Document doc = this.doLoadDocument(inputSource, resource);
            //@2這里是啟動(dòng)對(duì)Bean定義解析的詳細(xì)過(guò)程,該解析過(guò)程會(huì)用到Spring的Bean配置規(guī)則
            return this.registerBeanDefinitions(doc, resource);
        } catch (BeanDefinitionStoreException var4) {
            throw var4;
        } catch (SAXParseException var5) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var5.getLineNumber() + " in XML document from " + resource + " is invalid", var5);
        } catch (SAXException var6) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var6);
        } catch (ParserConfigurationException var7) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var7);
        } catch (IOException var8) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var8);
        } catch (Throwable var9) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var9);
        }
    }

@1

//使用標(biāo)準(zhǔn)的JAXP將載入的Bean定義資源轉(zhuǎn)換成document對(duì)象
 public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
        //創(chuàng)建文件解析器工廠 
        DocumentBuilderFactory factory = this.createDocumentBuilderFactory(validationMode, namespaceAware);
        if(logger.isDebugEnabled()) {
            logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
        }
        //創(chuàng)建文檔解析器  
        DocumentBuilder builder = this.createDocumentBuilder(factory, entityResolver, errorHandler);
        //解析Spring的Bean定義資源
        return builder.parse(inputSource);
    }

    protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware) throws ParserConfigurationException {
     //創(chuàng)建文檔解析工廠
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(namespaceAware);
        if(validationMode != 0) {
           //設(shè)置解析XML的校驗(yàn) 
            factory.setValidating(true);
            if(validationMode == 3) {
                factory.setNamespaceAware(true);

                try {
                    factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema");
                } catch (IllegalArgumentException var6) {
                    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(var6);
                    throw pcex;
                }
            }
        }

        return factory;
    } 

Spring IoC容器根據(jù)定位的Bean定義資源文件,將其加載讀入并轉(zhuǎn)換成為Document對(duì)象過(guò)程完成。

下面是 Spring IoC容器將載入的Bean定義資源文件轉(zhuǎn)換為Document對(duì)象之后,是如何將其解析為Spring IoC管理的Bean對(duì)象并將其注冊(cè)到容器中的
@2

 //按照Spring的Bean語(yǔ)義要求將Bean定義資源解析并轉(zhuǎn)換為容器內(nèi)部數(shù)據(jù)結(jié)構(gòu)
 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        /得到BeanDefinitionDocumentReader來(lái)對(duì)xml格式的BeanDefinition解析 
        BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
        //獲得容器中注冊(cè)的Bean數(shù)量
        int countBefore = this.getRegistry().getBeanDefinitionCount();
        //解析過(guò)程入口,這里使用了委派模式,BeanDefinitionDocumentReader只是個(gè)接口
        //具體的解析實(shí)現(xiàn)過(guò)程有實(shí)現(xiàn)類DefaultBeanDefinitionDocumentReader完成
        //@1
        documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
        /統(tǒng)計(jì)解析的Bean數(shù)量 
        return this.getRegistry().getBeanDefinitionCount() - countBefore;
    }
    //創(chuàng)建BeanDefinitionDocumentReader對(duì)象,解析Document對(duì)象
    protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
        return (BeanDefinitionDocumentReader)BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
    }
    

DefaultBeanDefinitionDocumentReader 解析document對(duì)象按照spring Bean的定義規(guī)則轉(zhuǎn)換為BeanDefinition
@1

 public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
    public static final String BEAN_ELEMENT = "bean";
    public static final String NESTED_BEANS_ELEMENT = "beans";
    public static final String ALIAS_ELEMENT = "alias";
    public static final String NAME_ATTRIBUTE = "name";
    public static final String ALIAS_ATTRIBUTE = "alias";
    public static final String IMPORT_ELEMENT = "import";
    public static final String RESOURCE_ATTRIBUTE = "resource";
    public static final String PROFILE_ATTRIBUTE = "profile";
    protected final Log logger = LogFactory.getLog(this.getClass());
    private XmlReaderContext readerContext;
    private BeanDefinitionParserDelegate delegate;

    public DefaultBeanDefinitionDocumentReader() {
    }
    //根據(jù)Spring DTD對(duì)Bean的定義規(guī)則解析Bean定義Document對(duì)象 
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        //獲得XML描述符 
        this.readerContext = readerContext;
        this.logger.debug("Loading bean definitions");
        //獲得Document的根元素 
        Element root = doc.getDocumentElement();
        this.doRegisterBeanDefinitions(root);
    }

    protected final XmlReaderContext getReaderContext() {
        return this.readerContext;
    }

    protected Object extractSource(Element ele) {
        return this.getReaderContext().extractSource(ele);
    }
    //具體的解析過(guò)程由BeanDefinitionParserDelegate實(shí)現(xiàn),  
    //BeanDefinitionParserDelegate中定義了Spring Bean定義XML文件的各種元素
    protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
        if(this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute("profile");
            if(StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
                if(!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if(this.logger.isInfoEnabled()) {
                        this.logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
                    }

                    return;
                }
            }
        }
        //在解析Bean定義之前,進(jìn)行自定義的解析,增強(qiáng)解析過(guò)程的可擴(kuò)展性
        this.preProcessXml(root);
        //從Document的根元素開始進(jìn)行Bean定義的Document對(duì)象
        this.parseBeanDefinitions(root, this.delegate);
        //在解析Bean定義之后,進(jìn)行自定義的解析,增加解析過(guò)程的可擴(kuò)展性
        this.postProcessXml(root);
        this.delegate = parent;
    }
    //創(chuàng)建BeanDefinitionParserDelegate,用于完成真正的解析過(guò)程
    protected BeanDefinitionParserDelegate createDelegate(XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
        BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
        //BeanDefinitionParserDelegate初始化Document根元素
        delegate.initDefaults(root, parentDelegate);
        return delegate;
    }
    //使用Spring的Bean規(guī)則從Document的根元素開始進(jìn)行Bean定義的Document對(duì)象
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
         //Bean定義的Document對(duì)象使用了Spring默認(rèn)的XML命名空間
        if(delegate.isDefaultNamespace(root)) {
           //獲取Bean定義的Document對(duì)象根元素的所有子節(jié)點(diǎn)
            NodeList nl = root.getChildNodes();

            for(int i = 0; i < nl.getLength(); ++i) {
                Node node = nl.item(i);
                //獲得Document節(jié)點(diǎn)是XML元素節(jié)點(diǎn)
                if(node instanceof Element) {
                    Element ele = (Element)node;
                    //Bean定義的Document的元素節(jié)點(diǎn)使用的是Spring默認(rèn)的XML命名空間
                    if(delegate.isDefaultNamespace(ele)) {
                        //使用Spring的Bean規(guī)則解析元素節(jié)點(diǎn)
                        this.parseDefaultElement(ele, delegate);
                    } else {
                       //沒有使用Spring默認(rèn)的XML命名空間,則使用用戶自定義的解//析規(guī)則解析元素節(jié)點(diǎn)
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {
            //Document的根節(jié)點(diǎn)沒有使用Spring默認(rèn)的命名空間,則使用用戶自定義的  
           //解析規(guī)則解析Document根節(jié)點(diǎn)
            delegate.parseCustomElement(root);
        }

    }

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        //如果元素節(jié)點(diǎn)是導(dǎo)入元素,進(jìn)行導(dǎo)入解析  
        if(delegate.nodeNameEquals(ele, "import")) {
            this.importBeanDefinitionResource(ele);
            
        } else if(delegate.nodeNameEquals(ele, "alias")) {
        //如果元素節(jié)點(diǎn)是別名元素,進(jìn)行別名解析 
            this.processAliasRegistration(ele);
        } else if(delegate.nodeNameEquals(ele, "bean")) {
        //元素節(jié)點(diǎn)既不是導(dǎo)入元素,也不是別名元素,即普通的元素,  
        //按照Spring的Bean規(guī)則解析元素  
            this.processBeanDefinition(ele, delegate);
        } else if(delegate.nodeNameEquals(ele, "beans")) {
         //如果元素節(jié)點(diǎn)是元素注冊(cè)為BeanDefintion
            this.doRegisterBeanDefinitions(ele);
        }

    }
    //解析導(dǎo)入元素,從給定的導(dǎo)入路徑加載Bean定義資源到Spring IoC容器中
    protected void importBeanDefinitionResource(Element ele) {
        /獲取給定的導(dǎo)入元素的location屬性 
        String location = ele.getAttribute("resource");
        if(!StringUtils.hasText(location)) {
            this.getReaderContext().error("Resource location must not be empty", ele);
        } else {
            //獲取location的值 
            location = this.getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
            Set actualResources = new LinkedHashSet(4);
            //標(biāo)識(shí)給定的導(dǎo)入元素的location是否是絕對(duì)路徑
            boolean absoluteLocation = false;

            try {
                absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
            } catch (URISyntaxException var11) {
                ;
            }

            int importCount;
            if(absoluteLocation) {
                try {
                    //使用資源讀入器加載給定路徑的Bean定義資源
                    importCount = this.getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
                    if(this.logger.isDebugEnabled()) {
                        this.logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
                    }
                } catch (BeanDefinitionStoreException var10) {
                    this.getReaderContext().error("Failed to import bean definitions from URL location [" + location + "]", ele, var10);
                }
            } else {
            //給定的導(dǎo)入元素的location是相對(duì)路徑  
                try {
                    Resource relativeResource = this.getReaderContext().getResource().createRelative(location);
                    if(relativeResource.exists()) {
                         //使用資源讀入器加載Bean定義資源 
                        importCount = this.getReaderContext().getReader().loadBeanDefinitions(relativeResource);
                        actualResources.add(relativeResource);
                    } else {
                        String baseLocation = this.getReaderContext().getResource().getURL().toString();
                        importCount = this.getReaderContext().getReader().loadBeanDefinitions(StringUtils.applyRelativePath(baseLocation, location), actualResources);
                    }

                    if(this.logger.isDebugEnabled()) {
                        this.logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
                    }
                } catch (IOException var8) {
                    this.getReaderContext().error("Failed to resolve current resource location", ele, var8);
                } catch (BeanDefinitionStoreException var9) {
                    this.getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]", ele, var9);
                }
            }
            //在解析完元素之后,發(fā)送容器導(dǎo)入其他資源處理完成事件
            Resource[] actResArray = (Resource[])actualResources.toArray(new Resource[actualResources.size()]);
            this.getReaderContext().fireImportProcessed(location, actResArray, this.extractSource(ele));
        }
    }
   //解析別名元素,為Bean向Spring IoC容器注冊(cè)別名  
    protected void processAliasRegistration(Element ele) {
        //獲取別名元素中name的屬性值
        String name = ele.getAttribute("name");
        //獲取別名元素中alias的屬性值  
        String alias = ele.getAttribute("alias");
        boolean valid = true;
        if(!StringUtils.hasText(name)) {
            this.getReaderContext().error("Name must not be empty", ele);
            valid = false;
        }

        if(!StringUtils.hasText(alias)) {
            this.getReaderContext().error("Alias must not be empty", ele);
            valid = false;
        }

        if(valid) {
            try {
                //向容器的資源讀入器注冊(cè)別名
                this.getReaderContext().getRegistry().registerAlias(name, alias);
            } catch (Exception var6) {
                this.getReaderContext().error("Failed to register alias "" + alias + "" for bean with name "" + name + """, ele, var6);
            }
            //在解析完元素之后,發(fā)送容器別名處理完成事件
            this.getReaderContext().fireAliasRegistered(name, alias, this.extractSource(ele));
        }

    } 
    //解析Bean定義資源Document對(duì)象的普通元素
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        // BeanDefinitionHolder是對(duì)BeanDefinition的封裝,即Bean定義的封裝類  
       //對(duì)Document對(duì)象中元素的解析由BeanDefinitionParserDelegate實(shí)現(xiàn)  BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if(bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

            try {
                //向Spring IoC容器注冊(cè)解析得到的Bean定義,這是Bean定義向IoC容器注冊(cè)的入口
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
            } catch (BeanDefinitionStoreException var5) {
                this.getReaderContext().error("Failed to register bean definition with name "" + bdHolder.getBeanName() + """, ele, var5);
            }
            //在完成向Spring IoC容器注冊(cè)解析得到的Bean定義之后,發(fā)送注冊(cè)事件
            this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }

    }

    protected void preProcessXml(Element root) {
    }

    protected void postProcessXml(Element root) {
    }
}

通過(guò)上述Spring IoC容器對(duì)載入的Bean定義Document解析可以看出,我們使用Spring時(shí),在Spring配置文件中可以使用元素來(lái)導(dǎo)入IoC容器所需要的其他資源,Spring IoC容器在解析時(shí)會(huì)首先將指定導(dǎo)入的資源加載進(jìn)容器中。使用別名時(shí),Spring IoC容器首先將別名元素所定義的別名注冊(cè)到容器中。
對(duì)于既不是元素,又不是元素的元素,即Spring配置文件中普通的元素的解析由BeanDefinitionParserDelegate類的parseBeanDefinitionElement方法來(lái)實(shí)現(xiàn)。

下面是針對(duì)對(duì)Bean元素的解析過(guò)程

    //解析Bean定義資源文件中的元素,這個(gè)方法中主要處理元素的id,name
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
        //獲取id屬性
        String id = ele.getAttribute("id");
        //獲取name屬性
        String nameAttr = ele.getAttribute("name");
        List aliases = new ArrayList();
        //將Bean元素的name屬性全部放到alias屬性里面
        if(StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, ",; ");
            aliases.addAll(Arrays.asList(nameArr));
        }

        String beanName = id;
        if(!StringUtils.hasText(id) && !aliases.isEmpty()) {
            beanName = (String)aliases.remove(0);
            if(this.logger.isDebugEnabled()) {
                this.logger.debug("No XML "id" specified - using "" + beanName + "" as bean name and " + aliases + " as aliases");
            }
        }

        if(containingBean == null) {
            //檢查bean元素的id和name是否是唯一的 
            this.checkNameUniqueness(beanName, aliases, ele);
        }
        //詳細(xì)對(duì)元素中配置的Bean定義進(jìn)行解析的地方
        AbstractBeanDefinition beanDefinition = this.parseBeanDefinitionElement(ele, beanName, containingBean);
        if(beanDefinition != null) {
            if(!StringUtils.hasText(beanName)) {
                try {
                    if(containingBean != null) {
                        //判斷如果沒有id和name屬性時(shí)候是否包含子元素的
                        beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
                    } else {
                        //如果元素中沒有配置id、別名或者name,且包含了子//元素,為解析的Bean使用別名向IoC容器注冊(cè)  
                        beanName = this.readerContext.generateBeanName(beanDefinition);
                        String beanClassName = beanDefinition.getBeanClassName();
                        if(beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                            aliases.add(beanClassName);
                        }
                    }

                    if(this.logger.isDebugEnabled()) {
                        this.logger.debug("Neither XML "id" nor "name" specified - using generated bean name [" + beanName + "]");
                    }
                } catch (Exception var9) {
                    this.error(var9.getMessage(), ele);
                    return null;
                }
            }

            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        } else {
            return null;
        }
    }
    //詳細(xì)解析bean元素的地方
    public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {
        this.parseState.push(new BeanEntry(beanName));
        String className = null;
        //是否包含class屬性
        if(ele.hasAttribute("class")) {
            //獲取class屬性值 不做實(shí)例化,bean的實(shí)例化是在第一次獲取bean的時(shí)候完成,這里只獲取class的值
            className = ele.getAttribute("class").trim();
        }

        try {
            String parent = null;
            //如果元素中配置了parent屬性,則獲取parent屬性的值
            if(ele.hasAttribute("parent")) {
                parent = ele.getAttribute("parent");
            }
           //根據(jù)元素配置的class名稱和parent屬性值創(chuàng)建BeanDefinition  
            //為載入Bean定義信息做準(zhǔn)備 
            AbstractBeanDefinition bd = this.createBeanDefinition(className, parent);
            //對(duì)當(dāng)前的元素中配置的一些屬性進(jìn)行解析和設(shè)置,如配置的單態(tài)(singleton)屬性等
            this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            //為元素解析的Bean設(shè)置description信息 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, "description"));
            //對(duì)元素的meta(元信息)屬性解析
            this.parseMetaElements(ele, bd);
            //對(duì)元素的lookup-method屬性解析
            this.parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            //對(duì)元素的replaced-method屬性解析
            this.parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
            //解析元素的構(gòu)造方法設(shè)置
            this.parseConstructorArgElements(ele, bd);
             //解析元素的設(shè)置
            this.parsePropertyElements(ele, bd);
             //解析元素的qualifier屬性
            this.parseQualifierElements(ele, bd);
            bd.setResource(this.readerContext.getResource());
            bd.setSource(this.extractSource(ele));
            AbstractBeanDefinition var7 = bd;
            return var7;
        } catch (ClassNotFoundException var13) {
            this.error("Bean class [" + className + "] not found", ele, var13);
        } catch (NoClassDefFoundError var14) {
            this.error("Class that bean class [" + className + "] depends on not found", ele, var14);
        } catch (Throwable var15) {
            this.error("Unexpected failure during bean definition parsing", ele, var15);
        } finally {
            this.parseState.pop();
        }

        return null;
    }

通過(guò)對(duì)上述源碼的分析,就會(huì)明白我們?cè)赟pring配置文件中元素的中配置的屬性就是通過(guò)該方法解析和設(shè)置到Bean中去的。
注意:在解析元素過(guò)程中沒有創(chuàng)建和實(shí)例化Bean對(duì)象,只是創(chuàng)建了Bean對(duì)象的定義類BeanDefinition,將元素中的配置信息設(shè)置到BeanDefinition中作為記錄,當(dāng)依賴注入時(shí)才使用這些記錄信息創(chuàng)建和實(shí)例化具體的Bean對(duì)象。
上面方法中一些對(duì)一些配置如元信息(meta)、qualifier等的解析,我們?cè)赟pring中配置時(shí)使用的也不多,我們?cè)谑褂肧pring的元素時(shí),配置最多的是屬性。

調(diào)用BeanDefinitionReaderUtils的registerBeanDefinition方法向IoC容器注冊(cè)解析的Bean

 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if(bdHolder != null) {
            //通過(guò)對(duì)document對(duì)象的解析和封裝返回一個(gè)BeanDefinitionHolder
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

            try {
                //通過(guò)這個(gè)holder來(lái)注冊(cè)bean對(duì)象
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
            } catch (BeanDefinitionStoreException var5) {
                this.getReaderContext().error("Failed to register bean definition with name "" + bdHolder.getBeanName() + """, ele, var5);
            }

            this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }

    }

當(dāng)調(diào)用BeanDefinitionReaderUtils向IoC容器注冊(cè)解析的BeanDefinition時(shí),真正完成注冊(cè)功能的是DefaultListableBeanFactory。

    public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
        String[] aliases = definitionHolder.getAliases();
        if(aliases != null) {
            String[] var4 = aliases;
            int var5 = aliases.length;

            for(int var6 = 0; var6 < var5; ++var6) {
                String alias = var4[var6];
                registry.registerAlias(beanName, alias);
            }
        }

    }

DefaultListableBeanFactory registerBeanDefinition方法

  //存儲(chǔ)注冊(cè)的俄BeanDefinition
 private final Map beanDefinitionMap = new ConcurrentHashMap(256);
 //向IoC容器注冊(cè)解析的BeanDefiniton
 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");
        //校驗(yàn)解析的BeanDefiniton
        if(beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition)beanDefinition).validate();
            } catch (BeanDefinitionValidationException var9) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var9);
            }
        }

        BeanDefinition oldBeanDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);
        if(oldBeanDefinition != null) {
            if(!this.isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean "" + beanName + "": There is already [" + oldBeanDefinition + "] bound.");
            }

            if(oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                if(this.logger.isWarnEnabled()) {
                    this.logger.warn("Overriding user-defined bean definition for bean "" + beanName + "" with a framework-generated bean definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
            } else if(!beanDefinition.equals(oldBeanDefinition)) {
                if(this.logger.isInfoEnabled()) {
                    this.logger.info("Overriding bean definition for bean "" + beanName + "" with a different definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
            } else if(this.logger.isDebugEnabled()) {
                this.logger.debug("Overriding bean definition for bean "" + beanName + "" with an equivalent definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
            }

            this.beanDefinitionMap.put(beanName, beanDefinition);
        } else {
            if(this.hasBeanCreationStarted()) {
                Map var4 = this.beanDefinitionMap;
                //注冊(cè)的過(guò)程中需要線程同步,以保證數(shù)據(jù)的一致性 
                synchronized(this.beanDefinitionMap) {
                    //把bean存放到map中
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if(this.manualSingletonNames.contains(beanName)) {
                        Set updatedSingletons = new LinkedHashSet(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            } else {
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }

            this.frozenBeanDefinitionNames = null;
        }

        if(oldBeanDefinition != null || this.containsSingleton(beanName)) {
            this.resetBeanDefinition(beanName);
        }

    }

至此,Bean定義資源文件中配置的Bean被解析過(guò)后,已經(jīng)注冊(cè)到IoC容器中,被容器管理起來(lái),真正完成了IoC容器初始化所做的全部工作。現(xiàn) 在IoC容器中已經(jīng)建立了整個(gè)Bean的配置信息,這些BeanDefinition信息已經(jīng)可以使用,并且可以被檢索,IoC容器的作用就是對(duì)這些注冊(cè)的Bean定義信息進(jìn)行處理和維護(hù)。這些的注冊(cè)的Bean定義信息是IoC容器控制反轉(zhuǎn)的基礎(chǔ),正是有了這些注冊(cè)的數(shù)據(jù),容器才可以進(jìn)行依賴注入。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/73960.html

相關(guān)文章

  • 重拾-Spring-IOC

    摘要:為何重拾使用了多年,但是對(duì)其底層的一些實(shí)現(xiàn)還是一知半解,一些概念比較模糊故決定重新拾起,加深對(duì)的認(rèn)識(shí)。小結(jié)是在完成創(chuàng)建后對(duì)其進(jìn)行后置處理的接口是在完成實(shí)例化對(duì)其進(jìn)行的后置處理接口是框架底層的核心接口,其提供了創(chuàng)建,獲取等核心功能。 為何重拾 使用了 Spring 多年,但是對(duì)其底層的一些實(shí)現(xiàn)還是一知半解,一些概念比較模糊;故決定重新拾起,加深對(duì) Spring 的認(rèn)識(shí)。 重拾計(jì)劃 spr...

    GraphQuery 評(píng)論0 收藏0
  • spring-ioc流程源碼解析

    摘要:中的流程中的方法是啟動(dòng)加載整個(gè)容器的關(guān)鍵方法。此函數(shù)第一步將流轉(zhuǎn)換成的,接下來(lái)就是解析,轉(zhuǎn)換成的然后注冊(cè)到中的一個(gè)中去?,F(xiàn)在馬上就要開始解析了,真正的解析就在方法,是指,標(biāo)簽就是此下的,所以會(huì)走。至此,的流程就走完了。 Spring中ioc的流程 AbstractApplicationContext 中的refresh()方法是啟動(dòng)加載整個(gè)容器的關(guān)鍵方法。 在refresh()方法中...

    zorpan 評(píng)論0 收藏0
  • 零基礎(chǔ)帶你看Spring源碼——IOC控制反轉(zhuǎn)

    摘要:依賴注入是向某個(gè)類或方法注入一個(gè)值,其中所用到的原理就是控制反轉(zhuǎn)。但發(fā)現(xiàn)更多時(shí)間是在調(diào)和的源碼。里面就是從中取出這個(gè),完成控制反轉(zhuǎn)的??刂品崔D(zhuǎn)的優(yōu)點(diǎn)最后來(lái)以我個(gè)人觀點(diǎn)談?wù)効刂品崔D(zhuǎn)的優(yōu)點(diǎn)吧??刂品崔D(zhuǎn)為了降低項(xiàng)目耦合,提高延伸性。 本章開始來(lái)學(xué)習(xí)下Spring的源碼,看看Spring框架最核心、最常用的功能是怎么實(shí)現(xiàn)的。網(wǎng)上介紹Spring,說(shuō)源碼的文章,大多數(shù)都是生搬硬推,都是直接看來(lái)的...

    wing324 評(píng)論0 收藏0
  • Spring入門看這一篇就夠了

    摘要:甲乙交易活動(dòng)不需要雙方見面,避免了雙方的互不信任造成交易失敗的問題。這就是的核心思想。統(tǒng)一配置,便于修改。帶參數(shù)的構(gòu)造函數(shù)創(chuàng)建對(duì)象首先,就要提供帶參數(shù)的構(gòu)造函數(shù)接下來(lái),關(guān)鍵是怎么配置文件了。 前言 前面已經(jīng)學(xué)習(xí)了Struts2和Hibernate框架了。接下來(lái)學(xué)習(xí)的是Spring框架...本博文主要是引入Spring框架... Spring介紹 Spring誕生: 創(chuàng)建Spring的...

    superw 評(píng)論0 收藏0
  • 美團(tuán)容器平臺(tái)架構(gòu)及容器技術(shù)實(shí)踐

    摘要:美團(tuán)的容器使用狀況是目前線上業(yè)務(wù)已經(jīng)超過(guò)個(gè)服務(wù),容器實(shí)例數(shù)超過(guò)個(gè),很多大并發(fā)低延時(shí)要求的核心鏈路服務(wù),已經(jīng)穩(wěn)定地運(yùn)行在之上。美團(tuán)容器平臺(tái)的基本架構(gòu)首先介紹一下美團(tuán)容器平臺(tái)的基礎(chǔ)架構(gòu),相信各家的容器平臺(tái)架構(gòu)大體都差不多。 本文根據(jù)美團(tuán)基礎(chǔ)架構(gòu)部/容器研發(fā)中心技術(shù)總監(jiān)歐陽(yáng)堅(jiān)在2018 QCon(全球軟件開發(fā)大會(huì))上的演講內(nèi)容整理而成。 背景 美團(tuán)的容器集群管理平臺(tái)叫做HULK。漫威動(dòng)畫里的...

    binaryTree 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<