• Spring-BeanFactory基本工作流程


      Spring容器的初始化过程

      (1)定位  主要是包括Reader结尾的文件,定位资源的位置。

      (2)加载  BeanDefinition等文件主要用于加载保存类信息和OOP关系的,加载资源的内容。

      (3)注册  包括Factory、Context等来进入注册环节。注册就是把用户所定义的Bean放到IOC容器(实质是个Map)中。

      我们可以看一下在Spring中举足轻重的ApplicationContext:

      上图是ApplicationContext的继承体系,其实我们从图中可以得知,Application的终极父类肯定是BeanFactory。我们可以看一下它的源码架构。

    public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
            MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
        //得到这个ApplicationContext的ID
        @Nullable
        String getId();
    
        //返回这个ApplicationContext所属的Application的名字
        String getApplicationName();
    
        /**
         * Return a friendly name for this context.
         */
        String getDisplayName();
    
        /**
         * Return the timestamp when this context was first loaded.
         */
        long getStartupDate();
    
        /**
         * Return the parent context, or {@code null} if there is no parent
         * and this is the root of the context hierarchy.
         * @return the parent context, or {@code null} if there is no parent
         */
        @Nullable
        ApplicationContext getParent();
    
        AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
    
    }

      ApplicationContext 允许上下文嵌套,通过保持父上下文可以维持一个上下文体系。对于Bean 的查找可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的Spring 应用提供了一个共享的Bean 定义环境。

      启动方式

      (1)main方法启动

      (2)DispatchServlet

      (3)FileSystem

      (4)Plugin

      (5)Listener

      首先我们可以通过一段执行的过程来探究一下Spring的BeanFactory是怎么进行工作的:首先我先定义一个ApplicationContext,作为入口。

    定位

    ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");

      然后他会用到ClassPathXmlApplicationContext的构造函数。那么我们来看一下它的构造函数。

    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
         super(parent);
            setConfigLocations(configLocations);
            if (refresh) {
                refresh();
            }
        }

      首先我们发现他在这里面调用了父类的构造函数,一直沿着查下去,从ClassPathXmlApplicationContext -> AbstractXmlApplicationContext -> AbstractRefreshableConfigApplicationContext -> AbstractRefreshableApplicationContext -> AbstractApplicationContext ,然后具体代码如下

    public AbstractApplicationContext(@Nullable ApplicationContext parent) {
            this();
            setParent(parent);
        }
    public AbstractApplicationContext() {
            this.resourcePatternResolver = getResourcePatternResolver();
        }

      这里,AbstractApplicationContext调用了它的构造方法,首先是将这个context用到的ResourcePatternResolver(Spring Source的加载器),用来读入SpringBean的定义资源文件。然后再回到ClassPathXmlApplicationContext的构造方法,

    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] = resolvePath(locations[i]).trim();
                }
            }
            else {
                this.configLocations = null;
            }
        }

      setConfigLocations(configLocations)的作用是用来解析Bean定义资源文件的路径,处理多个资源文件字符串数组,并将它保存到成员变量里去。这个存放的东西,等到我们在后面需要getResource的时候,就要用到(在loadBeanDefinitions方法里面)。

      返回上面的构造方法,往下走,之后就是初始化中最核心的一块了,会先判断初始化的情况,如果已经被初始化了,那么就不会执行下面的东西,这样可以保证IOC容器不会被反复的初始化,也能保证IOC容器的单例。然后如果没有被初始化,则会执行refresh()方法,它的作用就是:在创建IOC 容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh 之后使用的是新建立起来的IOC 容器。refresh 的作用类似于对IOC 容器的重启,在新建立好的容器中对容器进行初始化,对Bean 定义资源进行载入。

    //Spring初始化中最核心的方法,把所有的Bean重新构造一遍    
    @Override
    public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // 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(); } } }

      首先执行了prepareRefresh方法,他的作用就是做一个刷新的准备工作。接下来执行的是ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 我们进入obtainFreshBeanFactory方法。

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
            refreshBeanFactory();
            ConfigurableListableBeanFactory beanFactory = getBeanFactory();
            if (logger.isDebugEnabled()) {
                logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
            }
            return beanFactory;
        }

      大致浏览一下这个方法,有前置操作,有后置操作,中间有着各种注册。

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
            refreshBeanFactory();
            ConfigurableListableBeanFactory beanFactory = getBeanFactory();
            if (logger.isDebugEnabled()) {
                logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
            }
            return beanFactory;
        }

      Bean由BeanFactory创建的。

      然后是refreshBeanFactory方法,这里使用的是委派设计模式,我们所在的AbstractApplicationContext类定义了一个抽象的refreshBeanFactory方法,实际上在代码执行时是调用的子类的该方法。具体类是AbstractRefreshableApplicationContext,当然他还是一个抽象类,只是继承了AbstractApplicationContext类。

    @Override
        protected final void refreshBeanFactory() throws BeansException {
            if (hasBeanFactory()) {
                destroyBeans();
                closeBeanFactory();
            }
            try {
                DefaultListableBeanFactory beanFactory = createBeanFactory();
                beanFactory.setSerializationId(getId());
                customizeBeanFactory(beanFactory);
                loadBeanDefinitions(beanFactory);
                synchronized (this.beanFactoryMonitor) {
                    this.beanFactory = beanFactory;
                }
            }
            catch (IOException ex) {
                throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
            }
        }

      首先判断此类持有的beanFactory变量是否存在,如果已经有了,就销毁掉容器里的bean,然后把容器关闭掉。这里直接执行try代码块,首先会创建一个BenFactory。

    protected DefaultListableBeanFactory createBeanFactory() {
            return new DefaultListableBeanFactory(getInternalParentBeanFactory());
        }

      调用createBeanFactory方法来获得一个DefaultListableBeanFactory,在Spring中,所有的BeanFactory都是最终由它来实现的,不管用的什么委派啊还是代理。它是继承BeanFactory后的第一个实体类。

      然后对创建好的BeanFactory配置一些参数,然后执行loadBeanDefinitions方法,用来调用载入Bean定义的方法。会进入AbstractXmlApplicationContext的loadBeanDefinitions方法。

    @Override
        protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
            // Create a new XmlBeanDefinitionReader for the given BeanFactory.
            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);
        }

      工厂初始化后,就要在这个方法里进行加载。在这个方法里,首先是创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容器使用该读取器读取Bean定义资源,然后后续是对读取器进行配置,最后一句的同名方法是对Bean读取器实现真正的加载。

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

      由于我们使用FileSystemXmlApplicationContext 作为例子分析,因此getConfigResources 的返回值为null,因此程序执行reader.loadBeanDefinitions(configLocations)分支。

    //重载方法,调用loadBeanDefinitions(String);
        @Override
        public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
            Assert.notNull(locations, "Location array must not be null");
            int counter = 0;
            for (String location : locations) {
                counter += loadBeanDefinitions(location);
            }
            return counter;
        }

    然后再一次进入loadBeanDefintions。

     1  public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
     2         ResourceLoader resourceLoader = this.getResourceLoader();
     3         if (resourceLoader == null) {
     4             throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
     5         } else {
     6             int loadCount;
     7             if (!(resourceLoader instanceof ResourcePatternResolver)) {
     8                 Resource resource = resourceLoader.getResource(location);
     9                 loadCount = this.loadBeanDefinitions((Resource)resource);
    10                 if (actualResources != null) {
    11                     actualResources.add(resource);
    12                 }
    13 
    14                 if (this.logger.isDebugEnabled()) {
    15                     this.logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
    16                 }
    17 
    18                 return loadCount;
    19             } else {
    20                 try {
    21                     Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
    22                     loadCount = this.loadBeanDefinitions(resources);
    23                     if (actualResources != null) {
    24                         Resource[] var6 = resources;
    25                         int var7 = resources.length;
    26 
    27                         for(int var8 = 0; var8 < var7; ++var8) {
    28                             Resource resource = var6[var8];
    29                             actualResources.add(resource);
    30                         }
    31                     }
    32 
    33                     if (this.logger.isDebugEnabled()) {
    34                         this.logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
    35                     }
    36 
    37                     return loadCount;
    38                 } catch (IOException var10) {
    39                     throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", var10);
    40                 }
    41             }
    42         }
    43     }

       然后再调用一次loadDefinations方法,也就是22行。其实我们的目的就是找到一个真正干活的方法,其他的只是不断地复用,在经过多个loadBeanDefinitions方法之后,重于在XmlBeanDefinitionReader这个类中的loadBeanDefinitions方法找到了目标物。

     1 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
     2         Assert.notNull(encodedResource, "EncodedResource must not be null");
     3         if (this.logger.isInfoEnabled()) {
     4             this.logger.info("Loading XML bean definitions from " + encodedResource.getResource());
     5         }
     6 
     7         Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
     8         if (currentResources == null) {
     9             currentResources = new HashSet(4);
    10             this.resourcesCurrentlyBeingLoaded.set(currentResources);
    11         }
    12 
    13         if (!((Set)currentResources).add(encodedResource)) {
    14             throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    15         } else {
    16             int var5;
    17             try {
    18                 InputStream inputStream = encodedResource.getResource().getInputStream();
    19 
    20                 try {
    21                     InputSource inputSource = new InputSource(inputStream);
    22                     if (encodedResource.getEncoding() != null) {
    23                         inputSource.setEncoding(encodedResource.getEncoding());
    24                     }
    25 
    26                     var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    27                 } finally {
    28                     inputStream.close();
    29                 }
    30             } catch (IOException var15) {
    31                 throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
    32             } finally {
    33                 ((Set)currentResources).remove(encodedResource);
    34                 if (((Set)currentResources).isEmpty()) {
    35                     this.resourcesCurrentlyBeingLoaded.remove();
    36                 }
    37             }
    38             return var5;
    39         }
    40     }

      在第26行终于发现了做“实事”的方法-----doLoadBeanDefinitions,这个方法就是从特定XML文件载入Bean定义资源的方法。

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
            try {
                Document doc = this.doLoadDocument(inputSource, resource);
                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);
            }
        }

      在这个方法里,看到了doLoadDocument方法,看名字就是来读取xml文件的信息。

      在后面是一个registerBeanDefinitions方法。我觉得Spring的方法命名是非常值得学习的,我们通过方法名字就可以得知这个方法到底是用来干嘛的。这个方法是用来将Bean定义方法转化为容器内部的数据结构的。

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

      然后我们再继续看一下内部的registerBeanDefinitions方法。这个方法来自于DefaultBeanDefinationDocumentReader。

    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
            this.readerContext = readerContext;
            this.logger.debug("Loading bean definitions");
            Element root = doc.getDocumentElement();
            this.doRegisterBeanDefinitions(root);
        }

      这个方法的作用就是用来解析Bean定义Document对象。在他里面又有一个做事的方法--doRegisterBeanDefinition,同样在DefaultBeanDefinationDocumentReader。

    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;
                    }
                }
            }
            this.preProcessXml(root);
            this.parseBeanDefinitions(root, this.delegate);
            this.postProcessXml(root);
            this.delegate = parent;
        }

      我们先来看一下parseBeanDefinitions方法,他传进去了一个delegate对象,在设计模式中,这个就是委派,他委派的是BeanDefinitionParserDelegate类,这个方法的作用就是从Document的根元素开始进行Bean定义的Document对象。比如说我们XML文件里对Bean的一些初始化定义,就是用来解析这个,把XML文件的内容变成BeanDefination。

    //使用Spring的Bean规则从Document的根元素开始进行Bean定义的Document对象
        protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
            //Bean定义的Document对象使用了Spring默认的XML命名空间
            if (delegate.isDefaultNamespace(root)) {
                //获取Bean定义的Document对象根元素的所有子节点
                NodeList nl = root.getChildNodes();
                for (int i = 0; i < nl.getLength(); i++) {
                    Node node = nl.item(i);
                    //获得Document节点是XML元素节点
                    if (node instanceof Element) {
                        Element ele = (Element) node;
                        //Bean定义的Document的元素节点使用的是Spring默认的XML命名空间
                        if (delegate.isDefaultNamespace(ele)) {
                            //使用Spring的Bean规则解析元素节点
                            parseDefaultElement(ele, delegate);
                        }
                        else {
                            //没有使用Spring默认的XML命名空间,则使用用户自定义的解//析规则解析元素节点
                            delegate.parseCustomElement(ele);
                        }
                    }
                }
            }
            else {
                //Document的根节点没有使用Spring默认的命名空间,则使用用户自定义的
                //解析规则解析Document根节点
                delegate.parseCustomElement(root);
            }
        }

      因为我们委派的是BeanDefinitionParserDelegate,所以在这个方法中调用的都是BeanDefinitionParserDelegate方法,本文开头讲过,Bean容器的初始化分为:定位、加载、注册。到这里我们已经找到了XML文件中关于Bean的描述,这时标志着我们已经进入了加载这个模块。这里讲XML文件的信息包装成BeanDefinition。在这里,首先会调用parseDefaultElement。

    加载

    //使用Spring的Bean规则解析Document元素节点
        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);
            }
            else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
                // recurse
                doRegisterBeanDefinitions(ele);
            }
        }

      在这里我们可以看到有一个do方法,这里又回到了之前的doRegisterDefinition。因为我们解析的事Bean,所以应该会进入第三个if,也就是processBeanDefinition方法。

    //解析Bean定义资源Document对象的普通元素
        protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
            BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
            // BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义的封装类
            //对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现
            // BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
            if (bdHolder != null) {
                bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
                try {
                    // Register the final decorated instance.
                    //向Spring IOC容器注册解析得到的Bean定义,这是Bean定义向IOC容器注册的入口
                    BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
                }
                catch (BeanDefinitionStoreException ex) {
                    getReaderContext().error("Failed to register bean definition with name '" +
                            bdHolder.getBeanName() + "'", ele, ex);
                }
                // Send registration event.
                //在完成向Spring IOC容器注册解析得到的Bean定义之后,发送注册事件
                getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
            }
        }

      在这里我们会建立一个BeanDefinitionHolder对象(BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义的封装类),那么我猜parseBeanDefinitionElement的作用一定是解析XML文件中<Bean>的属性。点进去看看验证一下吧。

     1 //解析Bean定义资源文件中的<Bean>元素,这个方法中主要处理<Bean>元素的id,name和别名属性
     2     @Nullable
     3     public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
     4         //获取<Bean>元素中的id属性值
     5         String id = ele.getAttribute(ID_ATTRIBUTE);
     6         //获取<Bean>元素中的name属性值
     7         String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
     8 
     9         //获取<Bean>元素中的alias属性值
    10         List<String> aliases = new ArrayList<>();
    11 
    12         //将<Bean>元素中的所有name属性值存放到别名中
    13         if (StringUtils.hasLength(nameAttr)) {
    14             String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
    15             aliases.addAll(Arrays.asList(nameArr));
    16         }
    17 
    18         String beanName = id;
    19         //如果<Bean>元素中没有配置id属性时,将别名中的第一个值赋值给beanName
    20         if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
    21             beanName = aliases.remove(0);
    22             if (logger.isDebugEnabled()) {
    23                 logger.debug("No XML 'id' specified - using '" + beanName +
    24                         "' as bean name and " + aliases + " as aliases");
    25             }
    26         }
    27 
    28         //检查<Bean>元素所配置的id或者name的唯一性,containingBean标识<Bean>
    29         //元素中是否包含子<Bean>元素
    30         if (containingBean == null) {
    31             //检查<Bean>元素所配置的id、name或者别名是否重复
    32             checkNameUniqueness(beanName, aliases, ele);
    33         }
    34 
    35         //详细对<Bean>元素中配置的Bean定义进行解析的地方
    36         AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    37         if (beanDefinition != null) {
    38             if (!StringUtils.hasText(beanName)) {
    39                 try {
    40                     if (containingBean != null) {
    41                         //如果<Bean>元素中没有配置id、别名或者name,且没有包含子元素
    42                         //<Bean>元素,为解析的Bean生成一个唯一beanName并注册
    43                         beanName = BeanDefinitionReaderUtils.generateBeanName(
    44                                 beanDefinition, this.readerContext.getRegistry(), true);
    45                     }
    46                     else {
    47                         //如果<Bean>元素中没有配置id、别名或者name,且包含了子元素
    48                         //<Bean>元素,为解析的Bean使用别名向IOC容器注册
    49                         beanName = this.readerContext.generateBeanName(beanDefinition);
    50                         // Register an alias for the plain bean class name, if still possible,
    51                         // if the generator returned the class name plus a suffix.
    52                         // This is expected for Spring 1.2/2.0 backwards compatibility.
    53                         //为解析的Bean使用别名注册时,为了向后兼容
    54                         //Spring1.2/2.0,给别名添加类名后缀
    55                         String beanClassName = beanDefinition.getBeanClassName();
    56                         if (beanClassName != null &&
    57                                 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
    58                                 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
    59                             aliases.add(beanClassName);
    60                         }
    61                     }
    62                     if (logger.isDebugEnabled()) {
    63                         logger.debug("Neither XML 'id' nor 'name' specified - " +
    64                                 "using generated bean name [" + beanName + "]");
    65                     }
    66                 }
    67                 catch (Exception ex) {
    68                     error(ex.getMessage(), ele);
    69                     return null;
    70                 }
    71             }
    72             String[] aliasesArray = StringUtils.toStringArray(aliases);
    73             return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    74         }
    75         //当解析出错时,返回null
    76         return null;
    77     }

      在这里面需要注意到的就是BeanDefinitionReaderUtils类,其中他有一个方法叫registerBeanDefinition,这里就代表我们容器初始化已经走到了注册阶段。

    注册

    //将解析的BeanDefinitionHold注册到容器中
        public static void registerBeanDefinition(
                BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
                throws BeanDefinitionStoreException {
    
            // Register bean definition under primary name.
            //获取解析的BeanDefinition的名称
            String beanName = definitionHolder.getBeanName();
            //向IOC容器注册BeanDefinition
            registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    
            // Register aliases for bean name, if any.
            //如果解析的BeanDefinition有别名,向容器为其注册别名
            String[] aliases = definitionHolder.getAliases();
            if (aliases != null) {
                for (String alias : aliases) {
                    registry.registerAlias(beanName, alias);
                }
            }
        }

       之前就有提过,Spring容器初始化中,实际上工作的方法都是以do开头的,实际上最忙的类就是DefaultListableBeanFactory,我们进入向IOC容器注册BeanDefinition的方法,也就是registerBeanDefinition方法。发现它就来到了类。

     1 //向IOC容器注册解析的BeanDefiniton
     2     @Override
     3     public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
     4             throws BeanDefinitionStoreException {
     5 
     6         Assert.hasText(beanName, "Bean name must not be empty");
     7         Assert.notNull(beanDefinition, "BeanDefinition must not be null");
     8 
     9         //校验解析的BeanDefiniton
    10         if (beanDefinition instanceof AbstractBeanDefinition) {
    11             try {
    12                 ((AbstractBeanDefinition) beanDefinition).validate();
    13             }
    14             catch (BeanDefinitionValidationException ex) {
    15                 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
    16                         "Validation of bean definition failed", ex);
    17             }
    18         }
    19 
    20         BeanDefinition oldBeanDefinition;
    21 
    22         oldBeanDefinition = this.beanDefinitionMap.get(beanName);
    23 
    24         if (oldBeanDefinition != null) {
    25             if (!isAllowBeanDefinitionOverriding()) {
    26                 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
    27                         "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
    28                         "': There is already [" + oldBeanDefinition + "] bound.");
    29             }
    30             else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
    31                 // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
    32                 if (this.logger.isWarnEnabled()) {
    33                     this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
    34                             "' with a framework-generated bean definition: replacing [" +
    35                             oldBeanDefinition + "] with [" + beanDefinition + "]");
    36                 }
    37             }
    38             else if (!beanDefinition.equals(oldBeanDefinition)) {
    39                 if (this.logger.isInfoEnabled()) {
    40                     this.logger.info("Overriding bean definition for bean '" + beanName +
    41                             "' with a different definition: replacing [" + oldBeanDefinition +
    42                             "] with [" + beanDefinition + "]");
    43                 }
    44             }
    45             else {
    46                 if (this.logger.isDebugEnabled()) {
    47                     this.logger.debug("Overriding bean definition for bean '" + beanName +
    48                             "' with an equivalent definition: replacing [" + oldBeanDefinition +
    49                             "] with [" + beanDefinition + "]");
    50                 }
    51             }
    52             this.beanDefinitionMap.put(beanName, beanDefinition);
    53         }
    54         else {
    55             if (hasBeanCreationStarted()) {
    56                 // Cannot modify startup-time collection elements anymore (for stable iteration)
    57                 //注册的过程中需要线程同步,以保证数据的一致性
    58                 synchronized (this.beanDefinitionMap) {
    59                     this.beanDefinitionMap.put(beanName, beanDefinition);
    60                     List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
    61                     updatedDefinitions.addAll(this.beanDefinitionNames);
    62                     updatedDefinitions.add(beanName);
    63                     this.beanDefinitionNames = updatedDefinitions;
    64                     if (this.manualSingletonNames.contains(beanName)) {
    65                         Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
    66                         updatedSingletons.remove(beanName);
    67                         this.manualSingletonNames = updatedSingletons;
    68                     }
    69                 }
    70             }
    71             else {
    72                 // Still in startup registration phase
    73                 this.beanDefinitionMap.put(beanName, beanDefinition);
    74                 this.beanDefinitionNames.add(beanName);
    75                 this.manualSingletonNames.remove(beanName);
    76             }
    77             this.frozenBeanDefinitionNames = null;
    78         }
    79 
    80         //检查是否有同名的BeanDefinition已经在IOC容器中注册
    81         if (oldBeanDefinition != null || containsSingleton(beanName)) {
    82             //重置所有已经注册过的BeanDefinition的缓存
    83             resetBeanDefinition(beanName);
    84         }
    85     }

      可以看到57行,它用到了线程安全,而那么beanDefinitionMap就是IOC容器!!!,它的来源是这样的

    //存储注册信息的BeanDefinition
        private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

      我们可以看到,它里面存放的是BeanDefinition。

      可以总结一下IOC容器初始化的过程:

      (1)定位:资源配置

      (2)加载:解析配置文件,把Bean包装到BeanDefinition里。

      (3)注册:把BeanDefinition对象放入IOC容器中。

      到这,IOC容器初始化就结束了。 

  • 相关阅读:
    vSphere vCenter的个人理解及问题
    服务器账号过期处理
    虚拟化初探引入
    win10虚拟机跨网段迁移
    win7远程执行win10的抓取代码
    Jenkins+Sonar质量门禁【实践篇pipeline版】
    ELK7.10 license过期处理
    php 0108
    php 0110
    php 0111
  • 原文地址:https://www.cnblogs.com/Jolivan/p/9168108.html
Copyright © 2020-2023  润新知