zoukankan      html  css  js  c++  java
  • 初始化IoC容器(Spring源码阅读)

     

    初始化IoC容器(Spring源码阅读)

    我们到底能走多远系列(31)

    扯淡:

      有个问题一直想问:各位你们的工资剩下来会怎么处理?已婚的,我知道工资永远都是不够的。未婚的你们,你们是怎么分配工资的?

      毕竟,对自己的收入的分配差不多体现了自己的现状,以及自己对自己未来有什么样的期许~

      

    主题:

      本人在阅读源码基本参考了《Spring技术内幕:深入解析 Spring架构与设计原理》,很不错的书籍,建议大家阅读。

      初始化IoC容器:1,resource定位  2,BeanDefinition载入  3,把BeanDefinition载入到IoC

    重要的解释:

      Spring中的IoC容器,所谓的容器核心就是个hashMap,准确的说是ConcurrentHashMap,键值对就是:<String, BeanDefinition> key是bean的name。

      那么BeanDefinition就是对外界bean描述的抽象,比如你写的<bean>标签,最会被抽象成一个BeanDefinition放进这个Map中去。

      所以一个完整的IoC容器来说,只要提供对这个Map的必要操作就可以了。一般DefaultListableBeanFactory 作为一个默认功能完整的IoC容器来使用。

      上面的解释已经很清楚了,IoC容器的初始化就可以理解成,我们有个xml描述了一些bean的属性,Spring把它读进来,按照自己的规则解析一边,把其中的Bean描述转换成一个个BeanDefinition放进Map中去就完成初始化了。

      FileSystemXmlApplicationContext为例子看一下源码中是如何实现的:(希望有兴趣的先看下Spring中BeanFactory下面的继承结构)

      我们会写类似下面的代码开始我们的Spring之旅:

    复制代码
    ApplicationContext ct = new FileSystemXmlApplicationContext("src/service/service.xml");
    复制代码


    FileSystemXmlApplicationContext
    的构造函数:

    复制代码
    public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
            this(new String[] {configLocation}, true, null);
        }
    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
                throws BeansException {
    
            super(parent);
            setConfigLocations(configLocations);
            if (refresh) {
                // 起点    
                refresh();
            }
        }
    复制代码

    调用的refresh()方法是AbstractApplicationContext的方法:(这个方法就像执行顺序图,描述好了先做什么,后做什么)

    复制代码
     View Code
    复制代码

    obtainFreshBeanFactory()方法:

    复制代码
        protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
         // 这个方法是在AbstractApplicationContext定义没有实现的abstract方法,这就是所谓的模板模式吧,后面很多地方这样实现的,可以学习下。
            refreshBeanFactory();
            ConfigurableListableBeanFactory beanFactory = getBeanFactory();
            if (logger.isDebugEnabled()) {
                logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
            }
            return beanFactory;
        }
    
      protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
    复制代码

    refreshBeanFactory()方法是谁实现的呢?子类去实现。AbstractRefreshableApplicationContext实现的:

    复制代码
        protected final void refreshBeanFactory() throws BeansException {
            if (hasBeanFactory()) {
                destroyBeans();
                closeBeanFactory();
            }
            try {
           // 这里我们就看到了,通过这个方法调用来初始化的,都使用了DefaultListableBeanFactory作为IoC容器 DefaultListableBeanFactory beanFactory
    = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory);
           // 这个方法就去完成load Bean啦,不过它也使用了子类的实现 loadBeanDefinitions(beanFactory);
    synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
    复制代码

    AbstractXmlApplicationContext实现的loadBeanDefinitions(DefaultListableBeanFactory beanFactory):

    复制代码
        protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
            // Create a new XmlBeanDefinitionReader for the given BeanFactory.
         // 更深一层后,这里就开始定义了Reader是什么了。我们要读取的xml文件,这个Reader可以帮助我们
            XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
            // Configure the bean definition reader with this context's
            // resource loading environment.
            beanDefinitionReader.setEnvironment(this.getEnvironment());
            beanDefinitionReader.setResourceLoader(this);
            beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    
            // Allow a subclass to provide custom initialization of the reader,
            // then proceed with actually loading the bean definitions.
            initBeanDefinitionReader(beanDefinitionReader);
         // 调用下面的的方法 loadBeanDefinitions(beanDefinitionReader); }
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) {
           // 把工作交给XmlBeanDefinitionReader完成 reader.loadBeanDefinitions(configLocations); } }
    复制代码

    通过XmlBeanDefinitionReader的父类AbstractBeanDefinitionReader的方法把String configLocations封装成Recource,然后调用XmlBeanDefinitionReader实现的loadBeanDefinitions(resource)方法。

    下面是父类的调用方法:

    复制代码
        public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
            Assert.notNull(resources, "Resource array must not be null");
            int counter = 0;
         // 这里的for就可以理解多个xml文件的情况了
         // 从外面看,一个bean首先是放在多个xml中的一个,然后是一个xml文件中的多个bean描述中的一个,这个也是定位到一个bean要走的路。
    for (Resource resource : resources) {
           // 交给子类实现 counter
    += loadBeanDefinitions(resource); } return counter; }
    复制代码
    子类实现
    复制代码
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
            return loadBeanDefinitions(new EncodedResource(resource));
        }
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
            Assert.notNull(encodedResource, "EncodedResource must not be null");
            if (logger.isInfoEnabled()) {
                logger.info("Loading XML bean definitions from " + encodedResource.getResource());
            }
    
            Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
            if (currentResources == null) {
                currentResources = new HashSet<EncodedResource>(4);
                this.resourcesCurrentlyBeingLoaded.set(currentResources);
            }
            if (!currentResources.add(encodedResource)) {
                throw new BeanDefinitionStoreException(
                        "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
            }
            try {
           // 用流的方式来读取,有点样子了哦~ InputStream inputStream
    = encodedResource.getResource().getInputStream(); try {
             // 只是一个封装类,里面有属性防这个流而已 InputSource inputSource
    = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); }
             // 我觉得到这里,看方法名就知道了,要开始do了,前面好像都没do嘛
    return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } } protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { int validationMode = getValidationModeForResource(resource);
           // 先解析成Document Document doc
    = this.documentLoader.loadDocument( inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
           // 按照Spring的规则开始解析,放进BeanDefinition
    return registerBeanDefinitions(doc, resource); } 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); } }
    复制代码

     XmlBeanDefinitionReader的registerBeanDefinitions方法:

    复制代码
        public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
            BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
            documentReader.setEnvironment(this.getEnvironment());
            int countBefore = getRegistry().getBeanDefinitionCount();
            documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
            return getRegistry().getBeanDefinitionCount() - countBefore;
        }
    复制代码

    上面代码可以看到:继续交给DefaultBeanDefinitionDocumentReader来解析Document:

    下面是详细的处理过程:

    复制代码
        public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
            this.readerContext = readerContext;
    
            logger.debug("Loading bean definitions");
            Element root = doc.getDocumentElement();
    
            doRegisterBeanDefinitions(root);
        }
        protected void doRegisterBeanDefinitions(Element root) {
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                Assert.state(this.environment != null, "environment property must not be null");
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                if (!this.environment.acceptsProfiles(specifiedProfiles)) {
                    return;
                }
            }
    
            // 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 = createHelper(readerContext, root, parent);
    
            preProcessXml(root);
            parseBeanDefinitions(root, this.delegate);
            postProcessXml(root);
    
            this.delegate = parent;
        }
        protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
            if (delegate.isDefaultNamespace(root)) {
                NodeList nl = root.getChildNodes();
           // 前面的注释中提到过bean定位时要从多个bean描述中一个个取,这个for循环就是了,
           //所以这个否循环里面的逻辑就是处理一个个bean的映射的,当然也有其他的标签,这里我们且认为只有bean吧
    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); } }
    
    
    
    
    复制代码

     parseDefaultElement方法:

    复制代码
        private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
         // 对各种标签的解析
            if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
                importBeanDefinitionResource(ele);
            }
            else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
                processAliasRegistration(ele);
            }
            else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
           // 看下个个方法 processBeanDefinition(ele, delegate); }
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse ==>注意这个beans就递归,有调用了前面的doRegisterBeanDefinitions方法 doRegisterBeanDefinitions(ele); } }
    复制代码

     processBeanDefinition方法:

    复制代码
        protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
         // 这里就出现了BeanDefinitionHolde 它封装了BeanDefinition
        // BeanDefinitionParserDelegate 来完成最底层的按照spring的规则解析
    BeanDefinitionHolder bdHolder
    = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. 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)); } }
    复制代码
    parseBeanDefinitionElement方法的代码:
    复制代码
     View Code
    复制代码

    上面解析的代码中有调用的一个方法parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean)带我们去解析bean中更加详细的属性:

    复制代码
    public AbstractBeanDefinition parseBeanDefinitionElement(
                Element ele, String beanName, BeanDefinition containingBean) {
    
            this.parseState.push(new BeanEntry(beanName));
    
            String className = null;
            if (ele.hasAttribute(CLASS_ATTRIBUTE)) {// class属性 
                className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
            }
    
            try {
                String parent = null;
                if (ele.hasAttribute(PARENT_ATTRIBUTE)) {// parent属性
                    parent = ele.getAttribute(PARENT_ATTRIBUTE);
                }
                AbstractBeanDefinition bd = createBeanDefinition(className, parent);
            // 下面parse方法解析其他的属性
                parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
                bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
    
                parseMetaElements(ele, bd);
                parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
                parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
    
                parseConstructorArgElements(ele, bd);
                parsePropertyElements(ele, bd);
                parseQualifierElements(ele, bd);
    
                bd.setResource(this.readerContext.getResource());
                bd.setSource(extractSource(ele));
    
                return bd;
            }
         // 下面的错误用spring有时候有看见过吧
    catch (ClassNotFoundException ex) { error("Bean class [" + className + "] not found", ele, ex); } catch (NoClassDefFoundError err) { error("Class that bean class [" + className + "] depends on not found", ele, err); } catch (Throwable ex) { error("Unexpected failure during bean definition parsing", ele, ex); } finally { this.parseState.pop(); } return null; }
    复制代码

    以上就是解析好了,然后就是放进传说中的IoC容器里啦:

    BeanDefinitionReaderUtils的registerBeanDefinition方法:

    复制代码
        public static void registerBeanDefinition(
                BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
                throws BeanDefinitionStoreException {
    
            // Register bean definition under primary name.
            String beanName = definitionHolder.getBeanName();
        // 调用了下BeanDefinitionRegistry 的方法,
        // 而这个registry就是DefaultListableBeanFactory,所有的BeanDefinition都要注册到它里面去呀...
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String aliase : aliases) { registry.registerAlias(beanName, aliase); } } }
    复制代码

    DefaultListableBeanFactory的registerBeanDefinition:

    复制代码
    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);
                }
            }
            synchronized (this.beanDefinitionMap) {
                Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
           // 保证加载bean唯一性
    if (oldBeanDefinition != null) { if (!this.allowBeanDefinitionOverriding) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound."); } else { if (this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean '" + beanName + "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } } else { this.beanDefinitionNames.add(beanName); this.frozenBeanDefinitionNames = null; }
           // 放到前面提到的ConcurrentHashMap里面去喽~
    this.beanDefinitionMap.put(beanName, beanDefinition); resetBeanDefinition(beanName); } }
    复制代码

    以上就是初始化IoC容器的过程。各位有兴趣可以自己debug进去一步步看既可以了。

    另外用maven的话直接用下面命令下载源码即可:

    复制代码
    mvn dependency:sources
    复制代码

    让我们继续前行

    ----------------------------------------------------------------------

    努力不一定成功,但不努力肯定不会成功。
    共勉。

     
     
    分类: Java
  • 相关阅读:
    Java核心技术 卷一 笔记四 库类的直接使用
    Java核心技术 卷一 笔记三 大数值及数组
    Java核心技术 卷一 笔记2 字符串的复制
    Java核心技术 卷一 笔记1
    修改css 样式后, hover事件 不生效
    修改 element ui input 输入框 样式不生效问题
    css3 计算属性
    Vue3 改动系列
    浏览器实现,向下滑动 鼠标滚轮,页面横向移动
    linux ceont0s7 vue 打包压缩图片 一直报错
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3252464.html
Copyright © 2011-2022 走看看