《Spring技術內幕》學習筆記4——IoC容器解析Bean定義資源並註冊解析後的Bean
原文連結http://blog.csdn.net/chjttony/article/details/6261708
1.通過前兩篇部落格的分析,我們已經瞭解了Spring IoC容器定位和載入Bean定義資原始檔的基本過程,接下來我們要繼續分析Spring IoC容器將載入的Bean定義資原始檔轉換為Document物件之後,是如何將其解析為Spring IoC管理的Bean物件並將其註冊到容器中的。
2.XmlBeanDefinitionReader解析載入的Bean定義資原始檔:
XmlBeanDefinitionReader類中的doLoadBeanDefinitions方法是從特定XML檔案中實際載入Bean定義資源的方法,該方法在載入Bean定義資源之後將其轉換為Document物件,接下來呼叫registerBeanDefinitions啟動Spring IoC容器對Bean定義的解析過程,registerBeanDefinitions方法原始碼如下:
- //按照Spring的Bean語義要求將Bean定義資源解析並轉換為容器內部資料結構
- public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
- //得到BeanDefinitionDocumentReader來對xml格式的BeanDefinition解析
- BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
- //獲得容器中註冊的Bean數量
- int countBefore = getRegistry().getBeanDefinitionCount();
- //解析過程入口,這裡使用了委派模式,BeanDefinitionDocumentReader只是個介面,//具體的解析實現過程有實現類DefaultBeanDefinitionDocumentReader完成
- documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
- //統計解析的Bean數量
- return getRegistry().getBeanDefinitionCount() - countBefore;
- }
- //建立BeanDefinitionDocumentReader物件,解析Document物件
- protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
- return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
- }
Bean定義資源的載入解析分為以下兩個過程:
首先,通過呼叫XML解析器將Bean定義資原始檔轉換得到Document物件,但是這些Document物件並沒有按照Spring的Bean規則進行解析。這一步是載入的過程
其次,在完成通用的XML解析之後,按照Spring的Bean規則對Document物件進行解析。
按照Spring的Bean規則對Document物件解析的過程是在介面BeanDefinitionDocumentReader的實現類DefaultBeanDefinitionDocumentReader中實現的。
BeanDefinitionDocumentReader介面通過registerBeanDefinitions方法呼叫其實現類DefaultBeanDefinitionDocumentReader對Document物件進行解析,解析的程式碼如下:
- //根據Spring DTD對Bean的定義規則解析Bean定義Document物件
- public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
- //獲得XML描述符
- this.readerContext = readerContext;
- logger.debug("Loading bean definitions");
- //獲得Document的根元素
- Element root = doc.getDocumentElement();
- //具體的解析過程由BeanDefinitionParserDelegate實現,
- //BeanDefinitionParserDelegate中定義了Spring Bean定義XML檔案的各種元素
- BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
- //在解析Bean定義之前,進行自定義的解析,增強解析過程的可擴充套件性
- preProcessXml(root);
- //從Document的根元素開始進行Bean定義的Document物件
- parseBeanDefinitions(root, delegate);
- //在解析Bean定義之後,進行自定義的解析,增加解析過程的可擴充套件性
- postProcessXml(root);
- }
- //建立BeanDefinitionParserDelegate,用於完成真正的解析過程
- protected BeanDefinitionParserDelegate createHelper(XmlReaderContext readerContext, Element root) {
- BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
- //BeanDefinitionParserDelegate初始化Document根元素
- delegate.initDefaults(root);
- return delegate;
- }
- //使用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);
- }
- }
- //使用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);
- }
- }
- //解析<Import>匯入元素,從給定的匯入路徑載入Bean定義資源到Spring IoC容器中
- protected void importBeanDefinitionResource(Element ele) {
- //獲取給定的匯入元素的location屬性
- String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
- //如果匯入元素的location屬性值為空,則沒有匯入任何資源,直接返回
- if (!StringUtils.hasText(location)) {
- getReaderContext().error("Resource location must not be empty", ele);
- return;
- }
- //使用系統變數值解析location屬性值
- location = SystemPropertyUtils.resolvePlaceholders(location);
- Set<Resource> actualResources = new LinkedHashSet<Resource>(4);
- //標識給定的匯入元素的location是否是絕對路徑
- boolean absoluteLocation = false;
- try {
- absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
- }
- catch (URISyntaxException ex) {
- //給定的匯入元素的location不是絕對路徑
- }
- //給定的匯入元素的location是絕對路徑
- if (absoluteLocation) {
- try {
- //使用資源讀入器載入給定路徑的Bean定義資源
- int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
- if (logger.isDebugEnabled()) {
- logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
- }
- }
- catch (BeanDefinitionStoreException ex) {
- getReaderContext().error(
- "Failed to import bean definitions from URL location [" + location + "]", ele, ex);
- }
- }
- else {
- //給定的匯入元素的location是相對路徑
- try {
- int importCount;
- //將給定匯入元素的location封裝為相對路徑資源
- Resource relativeResource = getReaderContext().getResource().createRelative(location);
- //封裝的相對路徑資源存在
- if (relativeResource.exists()) {
- //使用資源讀入器載入Bean定義資源
- importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
- actualResources.add(relativeResource);
- }
- //封裝的相對路徑資源不存在
- else {
- //獲取Spring IoC容器資源讀入器的基本路徑
- String baseLocation = getReaderContext().getResource().getURL().toString();
- //根據Spring IoC容器資源讀入器的基本路徑載入給定匯入
- //路徑的資源
- importCount = getReaderContext().getReader().loadBeanDefinitions(
- StringUtils.applyRelativePath(baseLocation, location), actualResources);
- }
- if (logger.isDebugEnabled()) {
- logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
- }
- }
- catch (IOException ex) {
- getReaderContext().error("Failed to resolve current resource location", ele, ex);
- }
- catch (BeanDefinitionStoreException ex) {
- getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
- ele, ex);
- }
- }
- Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
- //在解析完<Import>元素之後,傳送容器匯入其他資源處理完成事件
- getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
- }
- //解析<Alias>別名元素,為Bean向Spring IoC容器註冊別名
- protected void processAliasRegistration(Element ele) {
- //獲取<Alias>別名元素中name的屬性值
- String name = ele.getAttribute(NAME_ATTRIBUTE);
- //獲取<Alias>別名元素中alias的屬性值
- String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
- boolean valid = true;
- //<alias>別名元素的name屬性值為空
- if (!StringUtils.hasText(name)) {
- getReaderContext().error("Name must not be empty", ele);
- valid = false;
- }
- //<alias>別名元素的alias屬性值為空
- if (!StringUtils.hasText(alias)) {
- getReaderContext().error("Alias must not be empty", ele);
- valid = false;
- }
- if (valid) {
- try {
- //向容器的資源讀入器註冊別名
- getReaderContext().getRegistry().registerAlias(name, alias);
- }
- catch (Exception ex) {
- getReaderContext().error("Failed to register alias '" + alias +
- "' for bean with name '" + name + "'", ele, ex);
- }
- //在解析完<Alias>元素之後,傳送容器別名處理完成事件
- getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
- }
- }
- //解析Bean定義資源Document物件的普通元素
- protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
- // BeanDefinitionHolder是對BeanDefinition的封裝,即Bean定義的封裝類
- //對Document物件中<Bean>元素的解析由BeanDefinitionParserDelegate實現 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
- if (bdHolder != null) {
- bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
- try {
- //向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);
- }
- //在完成向Spring IoC容器註冊解析得到的Bean定義之後,傳送註冊事件
- getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
- }
- }
通過上述Spring IoC容器對載入的Bean定義Document解析可以看出,我們使用Spring時,在Spring配置檔案中可以使用<Import>元素來匯入IoC容器所需要的其他資源,Spring IoC容器在解析時會首先將指定匯入的資源載入進容器中。使用<Ailas>別名時,Spring IoC容器首先將別名元素所定義的別名註冊到容器中。
對於既不是<Import>元素,又不是<Alias>元素的元素,即Spring配置檔案中普通的<Bean>元素的解析由BeanDefinitionParserDelegate類的parseBeanDefinitionElement方法來實現。
Bean定義資原始檔中的<Import>和<Alias>元素解析在DefaultBeanDefinitionDocumentReader中已經完成,對Bean定義資原始檔中使用最多的<Bean>元素交由BeanDefinitionParserDelegate來解析,其解析實現的原始碼如下:
- //解析<Bean>元素的入口
- public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
- return parseBeanDefinitionElement(ele, null);
- }
- //解析Bean定義資原始檔中的<Bean>元素,這個方法中主要處理<Bean>元素的id,name
- //和別名屬性
- public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
- //獲取<Bean>元素中的id屬性值
- String id = ele.getAttribute(ID_ATTRIBUTE);
- //獲取<Bean>元素中的name屬性值
- String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
- ////獲取<Bean>元素中的alias屬性值
- List<String> aliases = new ArrayList<String>();
- //將<Bean>元素中的所有name屬性值存放到別名中
- if (StringUtils.hasLength(nameAttr)) {
- String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);
- aliases.addAll(Arrays.asList(nameArr));
- }
- String beanName = id;
- //如果<Bean>元素中沒有配置id屬性時,將別名中的第一個值賦值給beanName
- 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");
- }
- }
- //檢查<Bean>元素所配置的id或者name的唯一性,containingBean標識<Bean>
- //元素中是否包含子<Bean>元素
- if (containingBean == null) {
- //檢查<Bean>元素所配置的id、name或者別名是否重複
- checkNameUniqueness(beanName, aliases, ele);
- }
- //詳細對<Bean>元素中配置的Bean定義進行解析的地方
- AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
- if (beanDefinition != null) {
- if (!StringUtils.hasText(beanName)) {
- try {
- if (containingBean != null) {
- //如果<Bean>元素中沒有配置id、別名或者name,且沒有包含子//<Bean>元素,為解析的Bean生成一個唯一beanName並註冊
- beanName = BeanDefinitionReaderUtils.generateBeanName(
- beanDefinition, this.readerContext.getRegistry(), true);
- }
- else {
- //如果<Bean>元素中沒有配置id、別名或者name,且包含了子//<Bean>元素,為解析的Bean使用別名向IoC容器註冊
- beanName = this.readerContext.generateBeanName(beanDefinition);
- //為解析的Bean使用別名註冊時,為了向後相容 //Spring1.2/2.0,給別名新增類名字尾
- 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);
- }
- //當解析出錯時,返回null
- return null;
- }
- //詳細對<Bean>元素中配置的Bean定義其他屬性進行解析,由於上面的方法中已經對//Bean的id、name和別名等屬性進行了處理,該方法中主要處理除這三個以外的其他屬性資料
- public AbstractBeanDefinition parseBeanDefinitionElement(
- Element ele, String beanName, BeanDefinition containingBean) {
- //記錄解析的<Bean>
- this.parseState.push(new BeanEntry(beanName));
- //這裡只讀取<Bean>元素中配置的class名字,然後載入到BeanDefinition中去
- //只是記錄配置的class名字,不做例項化,物件的例項化在依賴注入時完成
- String className = null;
- if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
- className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
- }
- try {
- String parent = null;
- //如果<Bean>元素中配置了parent屬性,則獲取parent屬性的值
- if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
- parent = ele.getAttribute(PARENT_ATTRIBUTE);
- }
- //根據<Bean>元素配置的class名稱和parent屬性值建立BeanDefinition
- //為載入Bean定義資訊做準備
- AbstractBeanDefinition bd = createBeanDefinition(className, parent);
- //對當前的<Bean>元素中配置的一些屬性進行解析和設定,如配置的單態(singleton)屬性等
- parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
- //為<Bean>元素解析的Bean設定description資訊 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
- //對<Bean>元素的meta(元資訊)屬性解析
- parseMetaElements(ele, bd);
- //對<Bean>元素的lookup-method屬性解析
- parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
- //對<Bean>元素的replaced-method屬性解析
- parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
- //解析<Bean>元素的構造方法設定
- parseConstructorArgElements(ele, bd);
- //解析<Bean>元素的<property>設定
- parsePropertyElements(ele, bd);
- //解析<Bean>元素的qualifier屬性
- parseQualifierElements(ele, bd);
- //為當前解析的Bean設定所需的資源和依賴物件
- 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();
- }
- //解析<Bean>元素出錯時,返回null
- return null;
- }
只要使用過Spring,對Spring配置檔案比較熟悉的人,通過對上述原始碼的分析,就會明白我們在Spring配置檔案中<Bean>元素的中配置的屬性就是通過該方法解析和設定到Bean中去的。
注意:在解析<Bean>元素過程中沒有建立和例項化Bean物件,只是建立了Bean物件的定義類BeanDefinition,將<Bean>元素中的配置資訊設定到BeanDefinition中作為記錄,當依賴注入時才使用這些記錄資訊建立和例項化具體的Bean物件。
上面方法中一些對一些配置如元資訊(meta)、qualifier等的解析,我們在Spring中配置時使用的也不多,我們在使用Spring的<Bean>元素時,配置最多的是<property>屬性,因此我們下面繼續分析原始碼,瞭解Bean的屬性在解析時是如何設定的。
5.BeanDefinitionParserDelegate解析<property>元素:
BeanDefinitionParserDelegate在解析<Bean>呼叫parsePropertyElements方法解析<Bean>元素中的<property>屬性子元素,解析原始碼如下:
- //解析<Bean>元素中的<property>子元素
- public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
- //獲取<Bean>元素中所有的子元素
- NodeList nl = beanEle.getChildNodes();
- for (int i = 0; i < nl.getLength(); i++) {
- Node node = nl.item(i);
- //如果子元素是<property>子元素,則呼叫解析<property>子元素方法解析
- if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
- parsePropertyElement((Element) node, bd);
- }
- }
- }
- //解析<property>元素
- public void parsePropertyElement(Element ele, BeanDefinition bd) {
- //獲取<property>元素的名字
- 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存在,則不進行解析,直接返回。
- //即如果在同一個Bean中配置同名的property,則只有第一個起作用
- if (bd.getPropertyValues().contains(propertyName)) {
- error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
- return;
- }
- //解析獲取property的值
- Object val = parsePropertyValue(ele, bd, propertyName);
- //根據property的名字和值建立property例項
- PropertyValue pv = new PropertyValue(propertyName, val);
- //解析<property>元素中的屬性
- parseMetaElements(ele, pv);
- pv.setSource(extractSource(ele));
- bd.getPropertyValues().addPropertyValue(pv);
- }
- finally {
- this.parseState.pop();
- }
- }
- //解析獲取property值
- public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
- String elementName = (propertyName != null) ?
- "<property> element for property '" + propertyName + "'" :
- "<constructor-arg> element";
- //獲取<property>的所有子元素,只能是其中一種型別:ref,value,list等
- NodeList nl = ele.getChildNodes();
- Element subElement = null;
- for (int i = 0; i < nl.getLength(); i++) {
- No