Spring源码解析之一 ------ 默认标签的解析注册(IOC的第一步)
来源:互联网 发布:阿里云监控系统 编辑:程序博客网 时间:2024/06/07 23:20
要看Spring bean加载的过程,最好的是Debug跟着流程走,有所侧重的看。首先需要的搭建本地环境和简单的项目工程,搭建的方法我放在这篇博客里。
http://blog.csdn.net/weililansehudiefei/article/details/73744017
我也是看了不少博客,Debug了很多次才终于理解整个过程。第一次看的时候,别心急,几天看完第一次都没关系。
我相信这篇文章,能让你看懂bean加载。
一、schema、xsd的认识
我们在xml里面配置bean的时候,一定会在xml的开始有类似如下的定义
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <bean id="userId" name="userName" class="com.heitian.ssm.model.User"></bean></beans>
可以看到整个xml是被<beans></beans>标签给封起来的,在<beans>里面定义了schcema。它由URI + LOCATION构成。比如上面的文件中,
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd前面的http://www.springframeword.org/shema/beans表示URI,后面的表示xsd文件的地址,xsd文件是描述相应的标签的。比如这里的spring-beans-3.1.xsd就是描述bean这个标签的定义和组成。具体的讲解会在下一部分,实现自定义标签时候讲解。现在知道就行。当然spring并不是直接就去xsd文件的链接去下载相应的xsd,而是先去本地找。在本地的spring.shemas文件中,有对应的本地地址关系
http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsdhttp\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsdhttp\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsdhttp\://www.springframework.org/schema/beans/spring-beans-3.1.xsd=org/springframework/beans/factory/xml/spring-beans-3.1.xsdhttp\://www.springframework.org/schema/beans/spring-beans-3.2.xsd=org/springframework/beans/factory/xml/spring-beans-3.2.xsdhttp\://www.springframework.org/schema/beans/spring-beans-4.0.xsd=org/springframework/beans/factory/xml/spring-beans-4.0.xsdhttp\://www.springframework.org/schema/beans/spring-beans-4.1.xsd=org/springframework/beans/factory/xml/spring-beans-4.1.xsdhttp\://www.springframework.org/schema/beans/spring-beans-4.2.xsd=org/springframework/beans/factory/xml/spring-beans-4.2.xsdhttp\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-4.2.xsdhttp\://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsdhttp\://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsdhttp\://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsdhttp\://www.springframework.org/schema/tool/spring-tool-3.1.xsd=org/springframework/beans/factory/xml/spring-tool-3.1.xsdhttp\://www.springframework.org/schema/tool/spring-tool-3.2.xsd=org/springframework/beans/factory/xml/spring-tool-3.2.xsdhttp\://www.springframework.org/schema/tool/spring-tool-4.0.xsd=org/springframework/beans/factory/xml/spring-tool-4.0.xsdhttp\://www.springframework.org/schema/tool/spring-tool-4.1.xsd=org/springframework/beans/factory/xml/spring-tool-4.1.xsdhttp\://www.springframework.org/schema/tool/spring-tool-4.2.xsd=org/springframework/beans/factory/xml/spring-tool-4.2.xsdhttp\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-4.2.xsdhttp\://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsdhttp\://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsdhttp\://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsdhttp\://www.springframework.org/schema/util/spring-util-3.1.xsd=org/springframework/beans/factory/xml/spring-util-3.1.xsdhttp\://www.springframework.org/schema/util/spring-util-3.2.xsd=org/springframework/beans/factory/xml/spring-util-3.2.xsdhttp\://www.springframework.org/schema/util/spring-util-4.0.xsd=org/springframework/beans/factory/xml/spring-util-4.0.xsdhttp\://www.springframework.org/schema/util/spring-util-4.1.xsd=org/springframework/beans/factory/xml/spring-util-4.1.xsdhttp\://www.springframework.org/schema/util/spring-util-4.2.xsd=org/springframework/beans/factory/xml/spring-util-4.2.xsdhttp\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-4.2.xsd
二、spring中bean的解析和注册
首先,因为我们是在ClassPath下xml文件的方式配置的,所以Debug后会进入ClassPathXmlApplicationContext中,
public ClassPathXmlApplicationContext(String configLocation) throws BeansException { this(new String[]{configLocation}, true, (ApplicationContext)null); }
然后会调用同类中的多态的构造方法,如下。configLocations就是我们配置的xml文件的地址
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); this.setConfigLocations(configLocations); if(refresh) { this.refresh(); } }
在这里,通过this.setConfigLocations(configLocations)方法,设置此次bean加载的配置文件位置。
然后进入到了refresh()方法。但从代码看,貌似进入的refresh()方法还是在ClassPathXmlApplicationContex里面,但是并不是。因为
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext因为继承关系,这个时候,进入的是AbstractXmlApplicationContext中的refresh()方法。下面这个图,说明了接下来整个程序的调用图。其实核心代码并不多,但是在去往核心代码之前,会做一系列的准备和校验以及相应数据的转换。
进入到refresh()方法,可以看到这里包含了bean的主要处理逻辑。而我们要进入的是this.obtainFreshBeanFactory()。
public void refresh() throws BeansException, IllegalStateException { Object var1 = this.startupShutdownMonitor; synchronized(this.startupShutdownMonitor) { this.prepareRefresh(); ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory(); this.prepareBeanFactory(beanFactory); try { this.postProcessBeanFactory(beanFactory); this.invokeBeanFactoryPostProcessors(beanFactory); this.registerBeanPostProcessors(beanFactory); this.initMessageSource(); this.initApplicationEventMulticaster(); this.onRefresh(); this.registerListeners(); this.finishBeanFactoryInitialization(beanFactory); this.finishRefresh(); } catch (BeansException var9) { if(this.logger.isWarnEnabled()) { this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9); } this.destroyBeans(); this.cancelRefresh(var9); throw var9; } finally { this.resetCommonCaches(); } } }
进入到obtainFreashBeanFactory方法,然后进入this.refreshBeanFactory();在refreshBeanFactory中,Spring创建了一个beanFactory,
然后进入loadBeanDefinitions(beanFactory)。
protected final void refreshBeanFactory() throws BeansException { if(this.hasBeanFactory()) { this.destroyBeans(); this.closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = this.createBeanFactory(); beanFactory.setSerializationId(this.getId()); this.customizeBeanFactory(beanFactory); 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); } }
在loadBeanDefinitions中,Spring为该beanFactory创建了一个beanDefinitionReader,看名字也知道这个reader是用来读取bean的定义信息。
然后进入this.loadBeanDefinitions(beanDefinitionReader)
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); this.initBeanDefinitionReader(beanDefinitionReader); this.loadBeanDefinitions(beanDefinitionReader);//进入这里 }进入之后,是如下的内容。在这里判断configResource是否为空,同时把第一步设置的configLocations,通过get方法读了出来,也就是配置文件spring.xml的地址和名字。
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);//进入这里 } }
连续进入loadBeanDefinition后,如图。这个时候在进入新的loadBeanDefinition之前,他会根据location的位置,获取Resource。
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException { 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)) { Resource resource = resourceLoader.getResource(location); 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 { Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location); 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); } } } }接下来进入进入后,resource文件被转成了encodedResource,
var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
这个方法是真正做事情的方法。凡是看到以do开头,那说明是真正做事情的。之前的都是各种转换和准备。需要说明的是,从resource到encodedResource再到inoputStream再到inputSource,这时候xml文件并没有真正读取。如果你是debug,可以看到这几个里面存放的都是spring.xml的加载路径。
进入真正的方法。
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<EncodedResource> 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 { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if(encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource()); //这里do开头的方法,才是做事情的方法 } finally { 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; } }进入后,我们发现通过inputSource和resource生成了doc对象,在doLoadDocument方法里,进行了文档的解析。文档解析的过程也有一些需要注意的地方,但是这里我们先不管它,进入registerBeanDefinitions(doc,resource)。
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); } }
进入后,定义了一个BeanDefiniDocumReader,这个documentReader用来读取doc对象,并注册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; }
然后进入doRegisterBeanDefinitons(root)。
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; this.logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); this.doRegisterBeanDefinitions(root);//进入 }进入之后,如下图,这个时候我们继续进入注释表明的地方。
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)) { return; } } } this.preProcessXml(root); this.parseBeanDefinitions(root, this.delegate);//进入 this.postProcessXml(root); this.delegate = parent; }
进入parseBeanDefinitions后,这里开始对Document对象里面的Element节点root,开始遍历解析。
Spring的默认标签会进入this.parseDefaultElement解析,我们自定义的标签会进入delegate.parseCustomElement解析。
自定义标签的解析,在另外一篇单独讲解。这里讲解spring自带标签的解析。
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if(delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for(int i = 0; i < nl.getLength(); ++i) { Node node = nl.item(i); if(node instanceof Element) { Element ele = (Element)node; if(delegate.isDefaultNamespace(ele)) { this.parseDefaultElement(ele, delegate);//进入 } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
进入之后,可以看到spring默认的标签只有四个:import、alias、bean、beans。在我们的spring.xml里面,我们只定义了root节点beans和一个子节点bean,所以进入bean的解析方法。
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if(delegate.nodeNameEquals(ele, "import")) { this.importBeanDefinitionResource(ele); } else if(delegate.nodeNameEquals(ele, "alias")) { this.processAliasRegistration(ele); } else if(delegate.nodeNameEquals(ele, "bean")) { this.processBeanDefinition(ele, delegate); //进入 } else if(delegate.nodeNameEquals(ele, "beans")) { this.doRegisterBeanDefinitions(ele); } }进入之后 如下,先获取了bean标签的 BeanDefinitionHolder,这个bdHolder是什么呢?我们知道bean标签里面,有name、id、class等属性,这个方法就是为了获取ele标签的属性。如果你想直接进入最终bean的注册地方,可以跳过下面橙色的地方。
我们先进入解析生成BeanDefinitionHolder的方法里,然后再回到核心的registerBeanDefinition。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if(bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);//进入 try { 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)); } }
进入这个,
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
看标签的属性是怎么生成的,相应的注释直接写在里面了。
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { String id = ele.getAttribute("id");//获取id属性,userId String nameAttr = ele.getAttribute("name");//获取name属性,userName List<String> aliases = new ArrayList(); //别名,我们自己在bean中配置的name,属于别名 if(StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, ",; "); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; //beanName是由id属性给的,而不是name属性 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) { this.checkNameUniqueness(beanName, aliases, ele); } AbstractBeanDefinition beanDefinition = this.parseBeanDefinitionElement(ele, beanName, containingBean);//进入 if(beanDefinition != null) { if(!StringUtils.hasText(beanName)) { try { if(containingBean != null) { beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true); } else { 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; } }
接下来进入this.parseBeanDefinitionElement中,在这个方法里,传入了beanName先被保存起来。然后获取class属性。
在this.createBeanDefinition中,通过反射获取class属性的类,然后保存在bd中。下方的注释说明了主要的作用,希望能进去看一下。
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null; if(ele.hasAttribute("class")) { className = ele.getAttribute("class").trim(); } try { String parent = null; if(ele.hasAttribute("parent")) { parent = ele.getAttribute("parent"); } AbstractBeanDefinition bd = this.createBeanDefinition(className, parent);//反射获取class属性指定的类,保存该类和类名 this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);//获取ele标签的所有属性,即bean的所有属性。 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, "description")); this.parseMetaElements(ele, bd); this.parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); this.parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); this.parseConstructorArgElements(ele, bd); this.parsePropertyElements(ele, bd); 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; }
上面一部分一下bdHolder的作用。
你需要知道的是bdHolder里面保存着该标签的所有属性。
然后回到正题,进入标签的注册阶段,这里就是bean标签的注册阶段。
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
进入这个方法,
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { String beanName = definitionHolder.getBeanName();//获取bean的名字,userId 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); } } }进入注册的核心registerBeanDefinition(...),终于到最终的注册方法上了。可能你之前也听过,容器其实是一个map,这里你将会看到它。
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 var9) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var9); } } BeanDefinition oldBeanDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);//从容器beanDefinitionMap中获取beanName对应的value if(oldBeanDefinition != null) {//如果不是null,说明该beanName已经在容器中存在了,抛异常 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 {//创建bean if(this.hasBeanCreationStarted()) { Map var4 = this.beanDefinitionMap; //存储bean的容器 synchronized(this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; if(this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { this.beanDefinitionMap.put(beanName, beanDefinition);//将bean注册到容器中 this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } if(oldBeanDefinition != null || this.containsSingleton(beanName)) { this.resetBeanDefinition(beanName); } }
上面就是核心的注册功能,在完成注册后,程序回到下面的地方.注册完bean后,开始进行别名alias的处理。这里还是有需要说道的地方,可别因为看到了核心的注册,后面别名这里
就不看了。进入下面注释说明的registerAlias()
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());//注册bean 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);//进入 } } }
进入后,代码如下。name就是beanName,在我的配置文件里就是userId,alias就是在配置文件里定义的name属性的值,也就是userName.我特意将id和name属性用不同的
名称,就是为了这里。
public void registerAlias(String name, String alias) { Assert.hasText(name, "'name' must not be empty");//name也就是 id属性的value:userId Assert.hasText(alias, "'alias' must not be empty");alias也就是配置文件中name属性的value:userName if(alias.equals(name)) {//id和name属性的值一样,就从存放别名的map中删除。 this.aliasMap.remove(alias); } else { String registeredName = (String)this.aliasMap.get(alias); if(registeredName != null) { if(registeredName.equals(name)) { return; } if(!this.allowAliasOverriding()) { throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'."); } } this.checkForAliasCircle(name, alias); this.aliasMap.put(alias, name);//将alisas(userName) 和 name(userId)存入别名map } }
到这里,整个流程基本就讲解完了。有疑问或者错误,欢迎留言。
- Spring源码解析之一 ------ 默认标签的解析注册(IOC的第一步)
- Spring源码解析之二 ------ 自定义标签的解析和注册(IOC的第一步)
- Spring源码解析-默认标签的解析
- 《Spring源码深度解析》阅读笔记5-默认标签的解析之bean标签的解析及注册
- spring源码深度解析(笔记二)--默认标签的解析
- Spring源码解析之默认标签的解析(一)
- Spring源码分析----IOC容器的实现(IoC容器的初始化过程(定位、载入解析、注册))
- Spring源码学习IOC(4):IoC容器解析Bean定义资源并注册解析后的Bean
- Spring源码解析-自定义标签的解析
- Spring默认标签解析及注册实现
- Spring IOC 源码分析-bean标签解析
- spring bean 标签的解析和注册
- Spring的IoC解析
- 《Spring源码深度解析》学习笔记——默认标签的解析
- spring源码剖析(二)Spring默认标签解析及注册实现
- spring ioc 源码解析
- Spring IOC 源码解析
- spring ioc源码解析
- linux查看网络信息命令
- “菜鱼”的第一篇博客!
- opencv 写视频时找不到编码器问题解决方法
- 关于ShareSDK微信的一个坑
- SpannableString相关工具类
- Spring源码解析之一 ------ 默认标签的解析注册(IOC的第一步)
- 端口号及分类协议号
- array to tree by js
- Spring依赖注入最易懂解释
- 常用的WebService网站
- @ResponseBody
- openssl源码安装
- HDU 1695 数论 容斥 欧拉函数 || 莫比乌斯反演
- 调通sina33下的AP6212A0版本的BT(V1.0)