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容器初始化就结束了。