Spring-IOC源码解读2.2-BeanDefinition的载入和解析过程
1. 对IOC容器来说这个载入过程就像是相当于把BeanDefinition定义的信息转化成Spring内部表示的数据结构。容器对bean的管理和依赖注入过程都是通过对其持有的BeanDefinition进行各种相关操作完成的,这些BeanDefinition在容器内部通过一个HashMap来维护。
2. 我们接着以DefaultListableBeanFactory的设计入手来分析这个载入和解析的过程,首先还是回到refresh()方法,然后沿着obtainFreshBeanFactory()->refreshBeanFactory()->loadBeanDefinitions()分析,下面是我们之前已经分析过的AbstarctXmlApplicationContext类的loadBeanDefinitions()方法:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 为指定的BeanFactory创建一个XmlBeanDefinitionReader XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 使用此上下文的资源加载环境配置BeanDefinitionReader,由于AbstartactXmlApplication继承了DefaultResourceLoader,所以这里的ResourceLoader传的是this beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 这是启动BeanDefinition信息载入的过程 initBeanDefinitionReader(beanDefinitionReader); //调用loadBeanDefinitions()载入BeanDefinition信息 loadBeanDefinitions(beanDefinitionReader); }
调用loadBeanDefinitions()时首先得到BeanDefinition信息的Resource定位,然后直接调用XmlBeanDefinitionReader来读取,具体的载入过程委托给BeanDefinitionReader完成。
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); } }
从上面的分析我们可以看到,初始化ClassPathXmlApplicationContext的时候是通过调用refresh()方法启动整个BeanDefinition的载入过程的,这个载入主要交给XmlBeanDefinitionReader来完成,同时具体的Resource载入是在XMLBeanDefinitionReader读入BeanDefinition时实现。
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; }
上面调用的AbstractBeanDefinitionReader类的抽象loadBeanDefinitions()方法,具体的实现在XmlBeanDefinitionReader类中,在xmlBeanDefinitionReader中首先得到代表xml文件的resource,这个resource封装了对xml文件的I/O操作,所以xmlReader在打开I/O流之后得到xml文件,然后就可以按照Spring定义的bean规则解析文档树,这个解析过程交给了BeanDefinitionParserDelegate来完成。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isInfoEnabled()) { logger.info("Loading XML bean definitions from " + encodedResource.getResource()); } Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<EncodedResource>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } //这里得到xml文件,并得到IO的inputStream进行读取 try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } } ------------------------------------> 我们看下具体的读取过程: protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException { try { int validationMode = getValidationModeForResource(resource); //获得xml文件的document对象 Document doc = this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); // 启动对BeanDefinition解析的详细过程,这个解析会使用Spring的bean配置规则 return registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(),"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); } catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(),"XML document from " + resource + " is invalid", ex); } catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(),"Parser configuration exception parsing XML from " + resource, ex); } catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(),"IOException parsing XML document from " + resource, ex); } catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(),"Unexpected exception parsing XML document from " + resource, ex); } }
我们看下registerBeanDefinitions()方法的实现:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // 得到documentReader 来对xml的BeanDefinition 进行解析,得到documentReader之后 ,为具体的Bean的解析准备好了数据 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); //统计载入的bean数量 int countBefore = getRegistry().getBeanDefinitionCount(); //具体的解析在registerBeanDefinitions中 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
上面可以看到载入分为两个部分,首先得到doc对象,这些doc对象并没有按照Spring的bean配置规则进行解析,完成通用的xml解析之后,才是按照bean规则解析的地方。看下DefaultBeanDefinitionDocumentReader类中的实现
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); BeanDefinitionParserDelegate delegate = createHelper(readerContext, root); preProcessXml(root); //解析BeanDefinition parseBeanDefinitions(root, delegate); postProcessXml(root); } ----------------------------------> 进入parseBeanDefinitions方法: 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)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
我们的测试代码会进入parseDefaultElement()方法:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); }// 进入解析bean的方法 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } } --------------------------------------->进入processBeanDefinition方法 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //BeandefinitionHolder是BeanDefinition的封装,封装了BeanDefinition,bean的名字和别名,用它来完成向IOC容器注册, //得到BeanDefinitionHodler就意味着BeanDefinition是通过BeanDefinitionParseDelegate对xml元素按照bean的规则解析得到的 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // 这里是向IOC容器解析注册得到BeanDefinition的地方 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // 在BeanDefinition向Ioc容器注册完成后发送消息 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
具体的BeanDefinition的解析是在BeanDefinitionParseDelegate中完成的,这个类包含了对各种SpringBean定义规则的处理。代码里面可以看到对BeanDefinition的处理,例如常见的id,name,attribute属性,把这些元素的值从xml文件读取解析之后设置到对应的BeanDefinitionHolder'中去,我们看下BeanDefinitionParseDelegate类对Bean元素的处理。
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { //取得定义的id,name,aliases属性的值 String id = ele.getAttribute(ID_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List<String> aliases = new ArrayList<String>(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0); if (logger.isDebugEnabled()) { logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases"); } } if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } //这里会触发对bean的详细解析 AbstractBeanDefinition beanDefinition = 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 (logger.isDebugEnabled()) { logger.debug("Neither XML 'id' nor 'name' specified - " + "using generated bean name [" + beanName + "]"); } } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null;
上面的解析过程可以看做根据xml文件对<bean>的定义生成BeanDefinition对象的过程,这个BeanDefinition对象中封装的数据大多都是与<bean>相关的,例如:init-method,destory-method,factory-method,beanClass,descriptor。有了这个BeanDefinition中分装的信息,容器才能对Bean配置进行处理以及实现容器的特性。对BeanDefinition的处理如下:
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); //这里只读取<bean>中设置的class名字,然后载入到BeanDefinition中,只是做个记录,并不涉及对象的实例化过程,对象的实例化实际是在依赖注入的时候完成 String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } try { String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } //这里生成需要的BeanDefinition对象,为Bean信息的载入做好准备 AbstractBeanDefinition bd = createBeanDefinition(className, parent); //这里对当前的Bean元素进行属性解析,并设置decription信息 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); // 这里解析bean的各种元素 parseMetaElements(ele, bd); parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); //解析构造函数 parseConstructorArgElements(ele, bd); // 解析property parsePropertyElements(ele, bd); parseQualifierElements(ele, bd); bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; } //下面这些异常我们启动容器的时候经常见到 catch (ClassNotFoundException ex) { error("Bean class [" + className + "] not found", ele, ex); } catch (NoClassDefFoundError err) { error("Class that bean class [" + className + "] depends on not found", ele, err); } catch (Throwable ex) { error("Unexpected failure during bean definition parsing", ele, ex); } finally { this.parseState.pop(); } return null; }
上面的代码是具体生成BeanDefinition的地方,我们看下具体如何解析Property属性的:
/对指定bean元素的property子元素集合进行解析 public void parsePropertyElements(Element beanEle, BeanDefinition bd) { //遍历bean元素下的property元素 NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) { //判断是property元素后进行解析 parsePropertyElement((Element) node, bd); } } } ------------------------------------> 进入parsePropertyElement public void parsePropertyElement(Element ele, BeanDefinition bd) { //取得propert元素的名字 String propertyName = ele.getAttribute(NAME_ATTRIBUTE); if (!StringUtils.hasLength(propertyName)) { error("Tag 'property' must have a 'name' attribute", ele); return; } this.parseState.push(new PropertyEntry(propertyName)); try { //如果同一个bean中已经有同名的property存在则不解析直接返回,即有两个的时候前面的起作用 if (bd.getPropertyValues().contains(propertyName)) { error("Multiple 'property' definitions for property '" + propertyName + "'", ele); return; } //解析property的值,返回的对象对应bean定义的property属性设置的解析结果,这个结果会封装到propertyVlaue对象中然后设置到BeanDefinitionHolder中 Object val = parsePropertyValue(ele, bd, propertyName); PropertyValue pv = new PropertyValue(propertyName, val); parseMetaElements(ele, pv); pv.setSource(extractSource(ele)); bd.getPropertyValues().addPropertyValue(pv); } finally { this.parseState.pop(); } } -----------------------------------> 进入parsePropertyValue public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) { String elementName = (propertyName != null) ? "<property> element for property '" + propertyName + "'" : "<constructor-arg> element"; // Should only have one child element: ref, value, list, etc. NodeList nl = ele.getChildNodes(); Element subElement = null; for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) && !nodeNameEquals(node, META_ELEMENT)) { // Child element is what we're looking for. if (subElement != null) { error(elementName + " must not contain more than one sub-element", ele); } else { subElement = (Element) node; } } } //判断property的属性是vlaue还是ref,不允许同时是value和ref boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); if ((hasRefAttribute && hasValueAttribute) || ((hasRefAttribute || hasValueAttribute) && subElement != null)) { error(elementName + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele); } //如果是ref,创建一个ref的数据对象RuntimeBeanReferenece,这个对象封装了ref的信息 if (hasRefAttribute) { String refName = ele.getAttribute(REF_ATTRIBUTE); if (!StringUtils.hasText(refName)) { error(elementName + " contains empty 'ref' attribute", ele); } RuntimeBeanReference ref = new RuntimeBeanReference(refName); ref.setSource(extractSource(ele)); return ref; } //如果是value,创建一个value的数据对象 else if (hasValueAttribute) { TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); valueHolder.setSource(extractSource(ele)); return valueHolder; } //如果还有子元素触犯对子元素的解析 else if (subElement != null) { return parsePropertySubElement(subElement, bd); } else { // Neither child element nor "ref" or "value" attribute found. error(elementName + " must specify a ref or value", ele); return null; } }
我们以List为例看下如何解析的:
public List parseListElement(Element collectionEle, BeanDefinition bd) { String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE); NodeList nl = collectionEle.getChildNodes(); ManagedList<Object> target = new ManagedList<Object>(nl.getLength()); target.setSource(extractSource(collectionEle)); target.setElementTypeName(defaultElementType); target.setMergeEnabled(parseMergeAttribute(collectionEle)); //具体的List元素的解析 parseCollectionElements(nl, target, bd, defaultElementType); return target; } -----------------------------> 进入parseCollectionElements方法 protected void parseCollectionElements( NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String defaultElementType) { //遍历所有元素节点,并判断节点类型是否为Element for (int i = 0; i < elementNodes.getLength(); i++) { Node node = elementNodes.item(i); if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) { //加入target中,target是个ManageList,同时触发对下一层子元素的解析过程,这是一个递归的过程 target.add(parsePropertySubElement((Element) node, bd, defaultElementType)); } } }
通过上面代码的分析,我们在xm文件定义的BeanDefinition被整个载入到IOC容器中,并在容器中建立了数据映射,经过以上的载入过程,IOC容器大致完成了管理bean对象的数据准备工作,但是重要的依赖注入过程还没发生,现在容器只有一些静态的配置信息,容器还没正式起作用,要发挥容器的作用,还需要完成数据向IOC的注册。