基於XmlBeanFactory加載bean分析:XmlBeanDefinitionReader(二)

 

 

查看一下XmlBeanDefinitionReader的類圖結構java

一、BeanDefinitionReadernode

public interface BeanDefinitionReader {
    BeanDefinitionRegistry getRegistry();

    ResourceLoader getResourceLoader();

    ClassLoader getBeanClassLoader();

    BeanNameGenerator getBeanNameGenerator();

    int loadBeanDefinitions(Resource var1) throws BeanDefinitionStoreException;

    int loadBeanDefinitions(Resource... var1) throws BeanDefinitionStoreException;

    int loadBeanDefinitions(String var1) throws BeanDefinitionStoreException;

    int loadBeanDefinitions(String... var1) throws BeanDefinitionStoreException;
}

二、EnvironmentCapableweb

public interface EnvironmentCapable {
    Environment getEnvironment();
}

這個接口主要是獲取系統環境變量等信息spring

三、AbstractBeanDefinitionReader BeanDefinition讀取(抽象類)安全

public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader {
    protected final Log logger = LogFactory.getLog(this.getClass());
    private final BeanDefinitionRegistry registry;
    private ResourceLoader resourceLoader;
    private ClassLoader beanClassLoader;
    private Environment environment;
    private BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();

    protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        this.registry = registry;
        if(this.registry instanceof ResourceLoader) {
            this.resourceLoader = (ResourceLoader)this.registry;
        } else {
            this.resourceLoader = new PathMatchingResourcePatternResolver();
        }

        if(this.registry instanceof EnvironmentCapable) {
            this.environment = ((EnvironmentCapable)this.registry).getEnvironment();
        } else {
            this.environment = new StandardEnvironment();
        }

    }
}
//這裏只列除了該類的主要參數以及構成函數,具體的實現須要查看源代碼在實際邏輯分析時也會貼出部分關鍵源代碼

RescoureLoader、Evnireonment等記錄app

四、XmlBeanDefinitionReader函數

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
    public static final int VALIDATION_NONE = 0;
    public static final int VALIDATION_AUTO = 1;
    public static final int VALIDATION_DTD = 2;
    public static final int VALIDATION_XSD = 3;
    private static final Constants constants = new Constants(XmlBeanDefinitionReader.class);
    private int validationMode = 1;
    private boolean namespaceAware = false;
	//這裏初始化的時候默認定義了DefaultBeanDefinitionDocumentReader
    private Class<?> documentReaderClass = DefaultBeanDefinitionDocumentReader.class;
    private ProblemReporter problemReporter = new FailFastProblemReporter();
	//事件通知:默認實現了EmptyReaderEventListener監聽器
    private ReaderEventListener eventListener = new EmptyReaderEventListener();
    private SourceExtractor sourceExtractor = new NullSourceExtractor();
    private NamespaceHandlerResolver namespaceHandlerResolver;
    private DocumentLoader documentLoader = new DefaultDocumentLoader();
    private EntityResolver entityResolver;
    private ErrorHandler errorHandler;
    private final XmlValidationModeDetector validationModeDetector;
    private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded;

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
        this.errorHandler = new SimpleSaxErrorHandler(this.logger);
		//驗證
        this.validationModeDetector = new XmlValidationModeDetector();
        this.resourcesCurrentlyBeingLoaded = new NamedThreadLocal("XML bean definition resources currently being loaded");
    }
}

-----------------------------------------具體分析--------------------------------------工具

BeanFactory bf = new XmlBeanFactory(new ClassPathResource("myresource/applicationContext.xml"));
org.springframework.beans.factory.xml.XmlBeanFactory
public class XmlBeanFactory extends DefaultListableBeanFactory {
    private final XmlBeanDefinitionReader reader;

    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, (BeanFactory)null);
    }

    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
		//初始化了XmlBeanDefinitionReader讀取器
        this.reader = new XmlBeanDefinitionReader(this);
		//並調用了loadBeanDefinitions方法
        this.reader.loadBeanDefinitions(resource);
    }
}
org.springframework.beans.factory.xml.XmlBeanDefinitionReader
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return this.loadBeanDefinitions(new EncodedResource(resource));
}

這裏咱們看到對Resource進行Encoded編碼post

private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded;
實現
this.resourcesCurrentlyBeingLoaded = new NamedThreadLocal("XML bean definition resources currently being loaded");
//這裏主要實現了對資源的加載
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if(this.logger.isInfoEnabled()) {
            this.logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }
        //這裏獲取當前線程的局部變量 咱們看到ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded裏面存放的是一個set 資源信息例如[applicationContext.xml,myTest.xml等等多個xml信息等]
		//初始化的時候去嘗試獲取一下resourcesCurrentlyBeingLoaded,若是爲空時,默認初始化了Set<EncodedResource>
		
        Object currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
        if(currentResources == null) {
            currentResources = new HashSet(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
		//若是加入失敗時,才拋出異常,因此一般狀況下都走的else流程,通常狀況都能添加到當前線程
		//這裏其實就是爲了不循環加載,若是重複加載了相同的文件就會拋出異常
        if(!((Set)currentResources).add(encodedResource)) {
            throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        } else {
            int var5;
            try {
			    //這裏就是將resource轉換爲IntputStream
                InputStream ex = encodedResource.getResource().getInputStream();

                try {
                    InputSource inputSource = new InputSource(ex);
                    if(encodedResource.getEncoding() != null) {
                        inputSource.setEncoding(encodedResource.getEncoding());
                    }
					//具體執行#處理邏輯分明
                    var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
                } finally {
                    ex.close();
                }
            } catch (IOException var15) {
                throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
            } finally {
                ((Set)currentResources).remove(encodedResource);
                if(((Set)currentResources).isEmpty()) {
                    this.resourcesCurrentlyBeingLoaded.remove();
                }

            }

            return var5;
        }
    }
//思考思考?
//加入resourcesCurrentlyBeingLoaded目的和意義
一、咱們看到resourcesCurrentlyBeingLoaded原本就是線程安全的由於使用了ThreadLocal,ThreadLocal存放的數據都是存放在Thread.ThreadLocalMap 中的。因此每一個線程會獨享本身的數據。

this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());學習

//方法的具體實現
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
	try {
	    //經過InputSource和Resource加載Document對象
		//注:加載document裏面設置了很複雜的邏輯處理一、包括對xml的驗證處理以及解析(有興趣能夠單獨瞭解)
		Document ex = this.doLoadDocument(inputSource, resource);
		//這裏根據Document和Resource執行註冊BeanDefinitions
		return this.registerBeanDefinitions(ex, resource);
	} catch (BeanDefinitionStoreException var4) {
		throw var4;
	} catch (SAXParseException var5) {
		throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var5.getLineNumber() + " in XML document from " + resource + " is invalid", var5);
	} catch (SAXException var6) {
		throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var6);
	} catch (ParserConfigurationException var7) {
		throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var7);
	} catch (IOException var8) {
		throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var8);
	} catch (Throwable var9) {
		throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var9);
	}
}

this.registerBeanDefinitions(ex, resource)

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    //建立一個BeanDefinition文檔讀取器,主要用於讀取document文檔【分支01】
	BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
	//獲取註冊前BeanDefinitionCount的數量
	int countBefore = this.getRegistry().getBeanDefinitionCount();
	//這裏使用了委託#即本身不執行具體操做而是交給別人(BeanDefinitionDocumentReader documentReader)執行
	//職責分明啊(之後實際開發應用要多學習這種思想:擴展性很好的)
	documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
	//this.getRegistry().getBeanDefinitionCount()表示註冊後BeanDefinitionCount的數量而後減去註冊前BeanDefinitionCount的數量,就表示本次註冊BeanDefinitionCount的數量
	return this.getRegistry().getBeanDefinitionCount() - countBefore;
}
【分支01】
private Class<?> documentReaderClass = DefaultBeanDefinitionDocumentReader.class;
//這裏執行很是簡單就是將DefaultBeanDefinitionDocumentReader轉化爲BeanDefinitionDocumentReader
//經過類圖結構咱們能夠發現DefaultBeanDefinitionDocumentReader爲BeanDefinitionDocumentReader的實現類而已。
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
	return (BeanDefinitionDocumentReader)BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
}

這裏咱們學習到了BeanUtils.instantiateClass(this.documentReaderClass)工具類

就是將一個class轉化爲一個bean,這裏很清楚確定用的是反射機制來實現。

documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));方法執行

由上面咱們分析出documentReader具體的實例對象就是DefaultBeanDefinitionDocumentReader

那麼由此咱們就進入DefaultBeanDefinitionDocumentReader.registerBeanDefinitions繼續研究具體執行了什麼?

private XmlReaderContext readerContext;
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    //設置XmlReaderContext 便於其餘方法直接能夠讀取
	this.readerContext = readerContext;
	this.logger.debug("Loading bean definitions");
	//獲取document根節點
	Element root = doc.getDocumentElement();
	//調用(doRegisterBeanDefinitions)方法
	this.doRegisterBeanDefinitions(root);
}

this.doRegisterBeanDefinitions(root);

private BeanDefinitionParserDelegate delegate;
protected void doRegisterBeanDefinitions(Element root) {
    //這裏嘗試去獲取delegate屬性,可是經過分析源代碼發如今初始化的時候並無設置delegate,因此這裏獲取到的delegate爲null
	BeanDefinitionParserDelegate parent = this.delegate;
	//這裏會建立一個delegate【分支02】
	this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
	/**
	* 這裏說了一些什麼事情呢?
	* 一、判斷當前document的命名空間其實就是判斷當前document的namespaceUri是否等於http://www.springframework.org/schema/beans,這裏主要的目地就是隻處理springbeans的xml
	* 二、判斷document是否包含profile屬性。這個屬性的目的主要是分環境開發,好比dev,uat,prd_uat,prd等環境時,咱們須要根據不一樣環境
	*   配置不一樣的參數。這裏能夠經過這種方式來,而後在web.xml中<context-param><context-name>Spring.profile.active</context-name><context-value>dev/uat/prd_uat/prd</context-value></context-param>
	*   而後spring啓動的時候就會根據這個配置來讀取不一樣的配置文件信息
	*
	*/
	if(this.delegate.isDefaultNamespace(root)) {
		String profileSpec = root.getAttribute("profile");
		if(StringUtils.hasText(profileSpec)) {
			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
			if(!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
				if(this.logger.isInfoEnabled()) {
					this.logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
				}

				return;
			}
		}
	}
	//這裏定義了一個處理器的模板方法
	this.preProcessXml(root);
	//根據當前document和BeanDefinitionParserDelegate delegate
	this.parseBeanDefinitions(root, this.delegate);
	//處理後的模板方法
	this.postProcessXml(root);
	this.delegate = parent;
}
//當前類未實現
protected void preProcessXml(Element root) {
}
////當前類未實現
protected void postProcessXml(Element root) {
}
//建立delegate【分支02】
protected BeanDefinitionParserDelegate createDelegate(XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
    //這裏根據readerContext建立了一個BeanDefinitionParserDelegate對象
	BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
	//而後根據Element root和 BeanDefinitionParserDelegate parentDelegate 初始化了一些設置
	//這裏到底作了一些什麼事情呢?【就是將element轉換爲BeanDefinitionParserDelegate】也就是說就是將xml文件配置的bean信息,轉化爲BeanDefinitionParserDelegate對象
	delegate.initDefaults(root, parentDelegate);
	return delegate;
}
//【能夠研究一下】BeanDefinitionParserDelegate它是一個單獨的類,在程序中就表示某一個xml轉換的java類而已

this.parseBeanDefinitions(root, this.delegate);

//經過以前分析咱們知道BeanDefinitionParserDelegate就是一個document處理器
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	//【分支3】就是判斷當前document是否擁有namespaceUri http://www.springframework.org/schema/beans
	if(delegate.isDefaultNamespace(root)) {
		//獲取當前document的全部節點信息
		NodeList nl = root.getChildNodes();

		for(int i = 0; i < nl.getLength(); ++i) {
			Node node = nl.item(i);
			//這裏只處理標籤因此須要判斷一下當前node是不是一個Element節點,若是是才繼續執行,不然跳過執行
			if(node instanceof Element) {
				Element ele = (Element)node;
				//再次驗證當前document是否擁有namespaceUri http://www.springframework.org/schema/beans若是包含就解析
				if(delegate.isDefaultNamespace(ele)) {
					this.parseDefaultElement(ele, delegate);
				} else {
					//若是不包含這裏採用的是解析器模式,根據
					delegate.parseCustomElement(ele);
				}
			}
		}
	} else {
		delegate.parseCustomElement(root);
	}

}
//【分支3】BeanDefinitionParserDelegate中的方法
public boolean isDefaultNamespace(String namespaceUri) {
        return !StringUtils.hasLength(namespaceUri) || "http://www.springframework.org/schema/beans".equals(namespaceUri);
}

public boolean isDefaultNamespace(Node node) {
	return this.isDefaultNamespace(this.getNamespaceURI(node));
}

下面章節會對

一、delegate.parseCustomElement(ele);

二、this.parseDefaultElement(ele, delegate);

進行分開分析,由於裏面實現太過複雜。寫在一塊兒不便理解

相關文章
相關標籤/搜索