【死闘Spring】–IOCの解析ビーン:解析importタグ

28902 ワード

原文の出典:http://cmsblogs.com
ブログ「死闘Spring」――IOCに登録されたBenDefinitionでは、Springには2つの解決方法があると分析しています.ルートノードまたはサブノードがデフォルトの名前空間を採用している場合は、parseDefaultElement()を呼び出してデフォルトのラベル解析を行い、そうでなければdelegate.parseCustomElement()方法を呼び出してカスタマイズ解析を行う.この二つの方法について以下のブログで詳しく説明します.まずデフォルトのタグ解析過程から始まります.ソースは以下の通りです.
	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);
		}
	}
方法の機能は一目瞭然で、それぞれ四つの異なるラベルを解析します.それぞれimport、alias、bean、beansです.私たちのドアは最初のラベルimportから始まります.
importラベルの取扱い
Springの配置ファイルを経験したことがある小さい仲間はすべて知っていて、もし工事が比較的に大きいならば、配置ファイルの維持は人に恐怖を感じさせることができて、ファイルは多すぎて、すべての配置を1つのspring.xmlの配置ファイルの中で置くと想像して、どのような後に感じるのかはとても明らかですか?このような状況に対してSpringはすべてサブモジュールの考えを提供しています.例えば、importタグを利用して、このようなspring.xmlを構築することができます.

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <import resource="spring-student.xml"/>
    <import resource="spring-student-dtd.xml"/>
beans>
spring.xmlプロファイルではimportタグを使用して他のモジュールのプロファイルを導入します.構成があれば、直接に対応するプロファイルを修正してもいいです.新しいモジュールがあれば直接にimportを追加してもいいです.このように構成後期メンテナンスの複雑さを大幅に簡略化し、管理も容易です.
Springは、importBeanDefinitionResource()方法を用いてimportタグの解析を行う.
    protected void importBeanDefinitionResource(Element ele) {
        //    resource      
        String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
        //   ,    
        if (!StringUtils.hasText(location)) {
            getReaderContext().error("Resource location must not be empty", ele);
            return;
        }

        //       ,    :"${user.dir}"
        location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);

        Set<Resource> actualResources = new LinkedHashSet<>(4);

        //    location            
        boolean absoluteLocation = false;
        try {
            absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
        }
        catch (URISyntaxException ex) {
            // cannot convert to an URI, considering the location relative
            // unless it is the well-known Spring prefix "classpath*:"
        }

        //     
        if (absoluteLocation) {
            try {
                //                
                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 {
            //                      
            try {
                int importCount;
                Resource relativeResource = getReaderContext().getResource().createRelative(location);
                if (relativeResource.exists()) {
                    importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
                    actualResources.add(relativeResource);
                }
                else {
                    String baseLocation = getReaderContext().getResource().getURL().toString();
                    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[0]);
        getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
    }
解析importの過程は比較的鮮明で、全体の過程は以下の通りである.
  • は、ソースの経路
  • を示すソース属性の値を取得する.
  • 解析パスにおけるシステム属性は、「$
  • は、リソースパスlocationが絶対パスか、それとも相対パス
  • かを判断する.
  • 絶対パスであれば、ビーンの解析プロセスを再帰的に呼び出し、別の解析を行う
  • .
  • 相対経路であれば、まず絶対経路を計算してResourceを取得し、その後に解析
  • を行う.
  • は、モニタに通知し、解析を完了する
  • .
    パスを判断する
    方法は、locationが相対パスか絶対パスかを判定するための方法である.
    absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
    
    絶対パスを判断するルールは以下の通りです.
  • は、クラスパス*またはクラスパスパス:先頭が絶対パス
  • である.
  • は、このlocationによってjava.net.URLを絶対パス
  • として構築することができる.
  • は、ロカモーション構造java.net.URIに基づいて、isAbsolute()を呼び出して、絶対パス
  • であるかどうかを判断する.
    絶対パス
    locationが絶対パスである場合はloadBeanDefinitions()を呼び出し、この方法はAbstractBenDefinitionReaderで定義される.
    	public int loadBeanDefinitions(String location, @Nullable 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;
    		}
    	}
    
    全体の論理は比較的簡単で、まずResource Loaderを取得し、その後、異なるResource Loaderによって異なる論理を実行し、主に複数のResourceが存在する可能性があるが、最終的にはXmlBeanDefinitionReader.loadBeanDefinitions()に復帰するので、これは再帰的なプロセスである.
    相対パス
    相対パスであれば、対応するResourceに従って、対応する絶対パスを計算し、その後、この経路に従ってResourceを構築し、このResourceが存在するならば、XmlBeanDefinitionReader.loadBeanDefinitions()を呼び出してBenDefinitionローディングを行う.そうでなければ、絶対locationを構築し、AbstractBeanDefinitionReader.loadBeanDefinitions()方法を呼び出し、絶対パスプロセスと同じである.
    ここで、importタグの解析が完了し、プロセス全体がより明確になった.source属性値を取得し、正しいリソースパスを得て、loadBeanDefinitions()方法で再帰的なBeanDefinitionローディングを呼び出す.