spring源码解析之IOC容器------加载和注册
Posted helei123
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring源码解析之IOC容器------加载和注册相关的知识,希望对你有一定的参考价值。
上一篇跟踪了IOC容器对配置文件的定位,现在我们继续跟踪代码,看看IOC容器是怎么加载和注册配置文件中的信息的。开始之前,首先我们先来了解一下IOC容器所使用的数据结构-------BeanDefinition,它是一个上层接口,有很多实现类,分别对应不同的数据载体。我们平时开发的时候,也会定义很多pojo类,来作为获取数据的载体。最常见的就是,从数据库中获取数据之后,使用一个定义的pojo来装载,然后我们就可以在程序中使用这个pojo类来编写各种业务逻辑。同样,IOC容器首先会读取配置的XML中各个节点,即各个标签元素,然后根据不同的标签元素,使用不同的数据结构来装载该元素中的各种属性的值。比如我们最熟悉的<bean>标签,就是使用AbstractBeanDefinition这个数据结构,接下来的分析中我们可以看到。
先回到上篇资源的定位那里,代码如下:
1 public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException 2 ResourceLoader resourceLoader = getResourceLoader(); 3 if (resourceLoader == null) 4 throw new BeanDefinitionStoreException( 5 "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); 6 7 8 if (resourceLoader instanceof ResourcePatternResolver) 9 // Resource pattern matching available. 10 try 11 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); 12 int loadCount = loadBeanDefinitions(resources); 13 if (actualResources != null) 14 for (Resource resource : resources) 15 actualResources.add(resource); 16 17 18 if (logger.isDebugEnabled()) 19 logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]"); 20 21 return loadCount; 22 23 catch (IOException ex) 24 throw new BeanDefinitionStoreException( 25 "Could not resolve bean definition resource pattern [" + location + "]", ex); 26 27 28 else 29 // 定位到资源之后,封装成一个resource对象 30 Resource resource = resourceLoader.getResource(location); 31 int loadCount = loadBeanDefinitions(resource); 32 if (actualResources != null) 33 actualResources.add(resource); 34 35 if (logger.isDebugEnabled()) 36 logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]"); 37 38 return loadCount; 39 40
进入loadBeanDefinitions(resource)方法,正式开始加载源码的跟踪:
1 @Override 2 public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException 3 return loadBeanDefinitions(new EncodedResource(resource)); 4
1 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException 2 Assert.notNull(encodedResource, "EncodedResource must not be null"); 3 if (logger.isInfoEnabled()) 4 logger.info("Loading XML bean definitions from " + encodedResource); 5 6 7 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); 8 if (currentResources == null) 9 currentResources = new HashSet<EncodedResource>(4); 10 this.resourcesCurrentlyBeingLoaded.set(currentResources); 11 12 if (!currentResources.add(encodedResource)) 13 throw new BeanDefinitionStoreException( 14 "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); 15 16 try 17 InputStream inputStream = encodedResource.getResource().getInputStream(); 18 try 19 InputSource inputSource = new InputSource(inputStream); 20 if (encodedResource.getEncoding() != null) 21 inputSource.setEncoding(encodedResource.getEncoding()); 22 23 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); 24 25 finally 26 inputStream.close(); 27 28 29 catch (IOException ex) 30 throw new BeanDefinitionStoreException( 31 "IOException parsing XML document from " + encodedResource.getResource(), ex); 32 33 finally 34 currentResources.remove(encodedResource); 35 if (currentResources.isEmpty()) 36 this.resourcesCurrentlyBeingLoaded.remove(); 37 38 39
进入doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法:
1 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) 2 throws BeanDefinitionStoreException 3 try 4 Document doc = doLoadDocument(inputSource, resource); 5 return registerBeanDefinitions(doc, resource); 6 7 catch (BeanDefinitionStoreException ex) 8 throw ex; 9 10 catch (SAXParseException ex) 11 throw new XmlBeanDefinitionStoreException(resource.getDescription(), 12 "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); 13 14 catch (SAXException ex) 15 throw new XmlBeanDefinitionStoreException(resource.getDescription(), 16 "XML document from " + resource + " is invalid", ex); 17 18 catch (ParserConfigurationException ex) 19 throw new BeanDefinitionStoreException(resource.getDescription(), 20 "Parser configuration exception parsing XML from " + resource, ex); 21 22 catch (IOException ex) 23 throw new BeanDefinitionStoreException(resource.getDescription(), 24 "IOException parsing XML document from " + resource, ex); 25 26 catch (Throwable ex) 27 throw new BeanDefinitionStoreException(resource.getDescription(), 28 "Unexpected exception parsing XML document from " + resource, ex); 29 30
继续进入registerBeanDefinitions(doc, resource)方法:
1 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException 2 //此时documentReader已经是DefaultBeanDefinitionDocumentReader类了 3 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); 4 int countBefore = getRegistry().getBeanDefinitionCount(); 5 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); 6 //返回当前注册的beanDefinition的个数 7 return getRegistry().getBeanDefinitionCount() - countBefore; 8
进入registerBeanDefinitions(doc, createReaderContext(resource))方法:
1 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) 2 this.readerContext = readerContext; 3 logger.debug("Loading bean definitions"); 4 Element root = doc.getDocumentElement(); 5 doRegisterBeanDefinitions(root); 6
进入doRegisterBeanDefinitions(root)方法:
1 protected void doRegisterBeanDefinitions(Element root) 2 // Any nested <beans> elements will cause recursion in this method. In 3 // order to propagate and preserve <beans> default-* attributes correctly, 4 // keep track of the current (parent) delegate, which may be null. Create 5 // the new (child) delegate with a reference to the parent for fallback purposes, 6 // then ultimately reset this.delegate back to its original (parent) reference. 7 // this behavior emulates a stack of delegates without actually necessitating one. 8 BeanDefinitionParserDelegate parent = this.delegate; 9 this.delegate = createDelegate(getReaderContext(), root, parent); 10 11 if (this.delegate.isDefaultNamespace(root)) 12 //profile属性平时使用非常少,该属性可以用于配置数据库的切换(常用),使用时,需要在web.xml中配置context-parm 13 //<context-parm> 14 // <parm-name>Spring.profiles.active</parm-name> 15 // <parm-value>dev(在applicationContext.xml中配置的profile属性的beans的profile属性值)</parm-name> 16 //</context-parm> 17 //在applicationContext.xml中的配置 18 //<beans profile="dev"> </beans> 19 //<beans profile="produce"> </beans> 20 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); 21 if (StringUtils.hasText(profileSpec)) 22 String[] specifiedProfiles = StringUtils.tokenizeToStringArray( 23 profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); 24 if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) 25 if (logger.isInfoEnabled()) 26 logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + 27 "] not matching: " + getReaderContext().getResource()); 28 29 return; 30 31 32 33 34 preProcessXml(root); 35 parseBeanDefinitions(root, this.delegate); 36 postProcessXml(root); 37 38 this.delegate = parent; 39
这里也用到了模板方法,preProcessXml(root)和postProcessXml(root)这两个方法都是空实现,是留给客户来实现自己的逻辑的。重点研究一下parseBeanDefinitions(root, this.delegate)方法:
1 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) 2 if (delegate.isDefaultNamespace(root)) 3 NodeList nl = root.getChildNodes(); 4 for (int i = 0; i < nl.getLength(); i++) 5 Node node = nl.item(i); 6 if (node instanceof Element) 7 Element ele = (Element) node; 8 if (delegate.isDefaultNamespace(ele)) 9 parseDefaultElement(ele, delegate); 10 11 else 12 delegate.parseCustomElement(ele); 13 14 15 16 17 else 18 delegate.parseCustomElement(root); 19 20
parseCustomElement(root)方法不需要怎么研究,我们平时几乎不会用到自定义的标签,所以只跟踪parseDefaultElement(ele, delegate)里面的代码:
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标签 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) processBeanDefinition(ele, delegate); //beans标签 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) // recurse doRegisterBeanDefinitions(ele);
可以看到,对于不同的标签,spring采用不同的策略进行处理,重点跟踪一下处理bean标签的方法processBeanDefinition(ele, delegate):
1 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 2 //委托给delegate去进行各种标签的解析,parseBeanDefinitionElement方法中包含了各种标签元素的解析, 3 //并将解析好的内容封装成BeanDefinitionHolder对象 4 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 5 if (bdHolder != null) 6 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); 7 try 8 // Register the final decorated instance. 9 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); 10 11 catch (BeanDefinitionStoreException ex) 12 getReaderContext().error("Failed to register bean definition with name ‘" + 13 bdHolder.getBeanName() + "‘", ele, ex); 14 15 // Send registration event. 16 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); 17 18
在这个方法中,delegate.parseBeanDefinitionElement(ele)是解析bean元素中各种属性的方法,registerBeanDefinition(bdHolder, getReaderContext().getRegistry())是将封装好的数据进行存储的方法。先看一下解析的方法:
1 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) 2 //获取bean标签的id属性的值 3 String id = ele.getAttribute(ID_ATTRIBUTE); 4 //获取bean标签上name属性的值 5 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); 6 7 List<String> aliases = new ArrayList<String>(); 8 if (StringUtils.hasLength(nameAttr)) 9 //将name的值进行分割,并将它们当作别名存到aliases中 10 String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); 11 aliases.addAll(Arrays.asList(nameArr)); 12 13 14 String beanName = id; 15 //如果bean标签的id没有值,但是name属性有值,则将name属性的第一个值当作id的值,并从aliases中将第一个别名移除掉 16 if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) 17 beanName = aliases.remove(0); 18 if (logger.isDebugEnabled()) 19 logger.debug("No XML ‘id‘ specified - using ‘" + beanName + 20 "‘ as bean name and " + aliases + " as aliases"); 21 22 23 24 if (containingBean == null) 25 //检查bean的唯一性 26 checkNameUniqueness(beanName, aliases, ele); 27 28 29 //这里已经是将XML中bean元素中的所有属性都封装到beanDefinition对象中了 30 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); 31 if (beanDefinition != null) 32 if (!StringUtils.hasText(beanName)) 33 try 34 if (containingBean != null) 35 beanName = BeanDefinitionReaderUtils.generateBeanName( 36 beanDefinition, this.readerContext.getRegistry(), true); 37 38 else 39 beanName = this.readerContext.generateBeanName(beanDefinition); 40 // Register an alias for the plain bean class name, if still possible, 41 // if the generator returned the class name plus a suffix. 42 // This is expected for Spring 1.2/2.0 backwards compatibility. 43 String beanClassName = beanDefinition.getBeanClassName(); 44 if (beanClassName != null && 45 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && 46 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) 47 aliases.add(beanClassName); 48 49 50 if (logger.isDebugEnabled()) 51 logger.debug("Neither XML ‘id‘ nor ‘name‘ specified - " + 52 "using generated bean name [" + beanName + "]"); 53 54 55 catch (Exception ex) 56 error(ex.getMessage(), ele); 57 return null; 58 59 60 String[] aliasesArray = StringUtils.toStringArray(aliases); 61 //最后将封装好的beanDefinition、它的id、以及它的别名一起封装成BeanDefinitionHolder对象返回 62 return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); 63 64 65 return null; 66
我们可以得到如下信息:
1、获取bean标签的id属性和name属性的值;
2、name属性是可以都有多个值的,以逗号或者分号分割;
3、如果id没有赋值,则取name的第一个值作为id的值。所以,我们一般都会给id赋值,这样效率高一些;
4、检查以这个id标识的bean是不是唯一的;
5、进行其他属性的解析,并最终封装测AbstractBeanDefinition对象,也就是我们前文中提到的数据结构;
6、最后封装成BeanDefinitionHolder对象之后返回。
进入parseBeanDefinitionElement(ele, beanName, containingBean)方法,看一下其他元素的解析过程:
1 public AbstractBeanDefinition parseBeanDefinitionElement( 2 Element ele, String beanName, BeanDefinition containingBean) 3 4 this.parseState.push(new BeanEntry(beanName)); 5 6 String className = null; 7 if (ele.hasAttribute(CLASS_ATTRIBUTE)) 8 className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); 9 10 11 try 12 String parent = null; 13 if (ele.hasAttribute(PARENT_ATTRIBUTE)) 14 parent = ele.getAttribute(PARENT_ATTRIBUTE); 15 16 AbstractBeanDefinition bd = createBeanDefinition(className, parent); 17 18 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); 19 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); 20 21 parseMetaElements(ele, bd); 22 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); 23 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); 24 25 parseConstructorArgElements(ele, bd); 26 parsePropertyElements(ele, bd); 27 parseQualifierElements(ele, bd); 28 29 bd.setResource(this.readerContext.getResource()); 30 bd.setSource(extractSource(ele)); 31 32 return bd; 33 34 catch (ClassNotFoundException ex) 35 error("Bean class [" + className + "] not found", ele, ex); 36 37 catch (NoClassDefFoundError err) 38 error("Class that bean class [" + className + "] depends on not found", ele, err); 39 40 catch (Throwable ex) 41 error("Unexpected failure during bean definition parsing", ele, ex); 42 43 finally 44 this.parseState.pop(); 45 46 47 return null; 48
解析封装成BeanDefinitionHolder对象之后,就可以进行注册了,先回到之前的processBeanDefinition(ele, delegate):
1 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 2 //委托给delegate去进行各种标签的解析,parseBeanDefinitionElement方法中包含了各种标签元素的解析, 3 //并将解析好的内容封装成BeanDefinitionHolder对象 4 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 5 if (bdHolder != null) 6 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); 7 try 8 // Register the final decorated instance. 9 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); 10 11 catch (BeanDefinitionStoreException ex) 12 getReaderContext().error("Failed to register bean definition with name ‘" + 13 bdHolder.getBeanName() + "‘", ele, ex); 14 15 // Send registration event. 16 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); 17 18
现在进入BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())方法进行分析:
1 public static void registerBeanDefinition( 2 BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) 3 throws BeanDefinitionStoreException 4 5 // Register bean definition under primary name. 6 String beanName = definitionHolder.getBeanName(); 7 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); 8 9 // Register aliases for bean name, if any. 10 String[] aliases = definitionHolder.getAliases(); 11 if (aliases != null) 12 for (String alias : aliases) 13 registry.registerAlias(beanName, alias); 14 15 16
这里的beanName就是之前封装好的bean的id。这个方法中分别以id和别名作为key来注册bean,其实就是存储在map中。
进入registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()),在其子类DefaultListableBeanFactory中有实现:
1 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 2 throws BeanDefinitionStoreException 3 4 Assert.hasText(beanName, "Bean name must not be empty"); 5 Assert.notNull(beanDefinition, "BeanDefinition must not be null"); 6 7 if (beanDefinition instanceof AbstractBeanDefinition) 8 try 9 ((AbstractBeanDefinition) beanDefinition).validate(); 10 11 catch (BeanDefinitionValidationException ex) 12 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, 13 "Validation of bean definition failed", ex); 14 15 16 17 BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); 18 if (existingDefinition != null) 19 if (!isAllowBeanDefinitionOverriding()) 20 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, 21 "Cannot register bean definition [" + beanDefinition + "] for bean ‘" + beanName + 22 "‘: There is already [" + existingDefinition + "] bound."); 23 24 else if (existingDefinition.getRole() < beanDefinition.getRole()) 25 // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE 26 if (logger.isWarnEnabled()) 27 logger.warn("Overriding user-defined bean definition for bean ‘" + beanName + 28 "‘ with a framework-generated bean definition: replacing [" + 29 existingDefinition + "] with [" + beanDefinition + "]"); 30 31 32 else if (!beanDefinition.equals(existingDefinition)) 33 if (logger.isInfoEnabled()) 34 logger.info("Overriding bean definition for bean ‘" + beanName + 35 "‘ with a different definition: replacing [" + existingDefinition + 36 "] with [" + beanDefinition + "]"); 37 38 39 else 40 if (logger.isDebugEnabled()) 41 logger.debug("Overriding bean definition for bean ‘" + beanName + 42 "‘ with an equivalent definition: replacing [" + existingDefinition + 43 "] with [" + beanDefinition + "]"); 44 45 46 this.beanDefinitionMap.put(beanName, beanDefinition); 47 48 else 49 if (hasBeanCreationStarted()) 50 // Cannot modify startup-time collection elements anymore (for stable iteration) 51 synchronized (this.beanDefinitionMap) 52 this.beanDefinitionMap.put(beanName, beanDefinition); 53 List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1); 54 updatedDefinitions.addAll(this.beanDefinitionNames); 55 updatedDefinitions.add(beanName); 56 this.beanDefinitionNames = updatedDefinitions; 57 if (this.manualSingletonNames.contains(beanName)) 58 Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames); 59 updatedSingletons.remove(beanName); 60 this.manualSingletonNames = updatedSingletons; 61 62 63 64 else 65 // Still in startup registration phase 66 this.beanDefinitionMap.put(beanName, beanDefinition); 67 this.beanDefinitionNames.add(beanName); 68 this.manualSingletonNames.remove(beanName); 69 70 this.frozenBeanDefinitionNames = null; 71 72 73 if (existingDefinition != null || containsSingleton(beanName)) 74 resetBeanDefinition(beanName); 75 76
我们可以看到:这个beanDefinitionMap就是用来存储解析好的bean的,以id作为key。至此,就将所有的bean标签解析好之后封装成BeanDefinition注册到了IOC容器中。但是,到目前为止,IOC容器并没有为我们将这些解析好的数据生成一个一个bean实例,我们仍然不能就这样直接使用。下一篇接着跟踪。
以上是关于spring源码解析之IOC容器------加载和注册的主要内容,如果未能解决你的问题,请参考以下文章
Spring IOC 容器源码分析 - getBean调用方法解析 -- Bean的初始化之BeanPostProcessor