前言
1.在講BeanDefinition的載入和解析之前,我們先來看看什么是BeanDefinition。
Bean對象在Spring中是以BeanDefinition來描述的,也就是說在Spring中,BeanDefinition用來表示bean對象。
2. 對於Spring IOC容器,BeanDefinition的載入過程,相當於把Spring的配置文件轉換成Spring的內部數據結構。
在上一篇中容器初始化各個父類方法調用圖,如下圖所示:
1.BeanDefinition的載入和解析
我們接着上一篇的代碼,在AbstractBeanDefinitionReader的loadBeanDefinitions()中,我們定位到了Resource[],看下代碼:
AbstractBeanDefinitionReader的loadBeanDefinitions
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException { ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); int loadCount = loadBeanDefinitions(resources); if (actualResources != null) { for (Resource resource : resources) { actualResources.add(resource); } } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]"); } return loadCount; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { // Can only load single resources by absolute URL. Resource resource = resourceLoader.getResource(location); int loadCount = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]"); } return loadCount; } }
1.1. 將xml文件轉換成DOM對象
通過getResouce()方法得到Resouce之后,那么就開始了真正加載Bean資源。因為Spring可以對應不同形式的BeanDefition,我們這里講的是xml的形式,所以就需要到xmlBeanDefinitonReader的實現中去看源碼。
XmlBeanDefinitionReader的loadBeanDefinitions()及doLoadBeanDefinitions()
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); }
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!"); } try { //將資源文件轉換為IO輸入流
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(); } } }
//該方法-從特定XML文件中實際載入Bean定義資源
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //將XML文件轉換為DOM對象
Document doc = doLoadDocument(inputSource, resource); //這里是啟動對Bean定義解析的詳細過程,該解析過程會用到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); } }
上面的過程完成了兩個動作:(1)資源文件轉換成IO輸入流;(2)將xml文件轉換成DOM對象。至於解析xml文件的方法,里面是通過創建解析工廠,設置xml解析校驗,通過工廠創建解析器,然后parse(),所以這個過程我們就不看了
1.2 按照Spring的Bean規則對Document對象進行解析
走主線:得到dom對象后,開始啟動對bean定義的詳細解析,來看registerBeanDefinition()方法
XmlBeanDefinitionReader的loadBeanDefinitions()方法
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); //解析過程入口,BeanDefinitionDocumentReader只是個接口,具體的解析實現過程有實現類
//DefaultBeanDefinitionDocumentReader完成
documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
我們總結下上面那么多繞人的代碼。其實就2個思想:
(1)首先,通過調用XML解析器將Bean定義資源文件轉換成DOM對象,但這些dom對象並沒有按照Spring的bean規則進行解析。這一步是載入的過程。
(2)其次,在完成通用的XML解析之后,按照Spring的bean規則對document對象進行解析。
按照Spring的Bean規則對Document對象解析的過程是在接口BeanDefinitionDocumentReader的實現類DefaultBeanDefinitionDocumentReader中實現的。OK,重點來了。(友情提示,會繞好幾個方法。)
DefaultBeanDefinitionDocumentReader的registerBeanDefinitions()及doRegisterBeanDefinitions()方法
@Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root); }
/** * Register each bean definition within the given root {@code <beans/>} element. */ protected void doRegisterBeanDefinitions(Element root) { // Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { return; } } } preProcessXml(root); parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }
/** * Parse the elements at the root level in the document: * "import", "alias", "bean". * @param root the DOM root element of the document */ 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); } }
上面這段代碼又墨跡了一會,這里主要是看節點元素是否是Spring的規范,因為它允許我們自定義解析規范,那么正常我們是利用Spring的規則,所以我們來看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>元素,按照Spring的Bean規則解析元素
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }
終於看到自己熟悉的一點東西了
1.3 Bean資源的解析
//解析bean資源
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //BeanDefinitionHolder是對BeanDefinition的封裝,包括BeanDefinition,beanName,aliases
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { //向SpringIOC容器注冊解析得到的bean定義
//這是bean定義向IOC容器注冊的入口,實際上是放到一個map里面
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
注意:在解析<Bean>元素過程中沒有創建和實例化Bean對象,只是創建了Bean對象的定義類型BeanDefinition,將<Bean>元素中的配置信息設置到BeanDefiniton作記錄,當依賴注入時才使用這些記錄信息創建和實例化具體的Bean對象。