1. 程式人生 > >Spring Bean的註冊

Spring Bean的註冊

Bean的定義

開始介紹:BeanFactory是最頂層的介面,它定義了IOC容器的基本功能規範,可以看到xml配置中的某些屬性了,比方說單例,多例,型別匹配,別名等。

BeanFactory有三個子類:ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。

每個介面都有各自的定義,它主要是為了區分在Spring內部在操作過程中物件的傳遞和轉化過程中,對物件的資料訪問所做的限制。

  • ListableBeanFactory介面表示這些Bean是可列表的。
  • HierarchicalBeanFactory表示的是這些Bean是有繼承關係的,也就是每個Bean有可能有父Bean。
  • AutowireCapableBeanFactory介面定義Bean的自動裝配規則。

四個介面共同定義了Bean的集合、Bean之間的關係、以及Bean行為。

從最頂級的BeanFactory開始看起。

  • DefaultListableBeanFactory
  • XmlBeanDefinitionReader

容器的初始化包括BeanDefinition的Resource定位、載入和註冊這三個基本的過程。

XmlBeanFactory來舉例吧。

        private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
	public XmlBeanFactory(Resource resource) throws BeansException {
		this(resource, null);
	}
	public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
		super(parentBeanFactory);
		this.reader.loadBeanDefinitions(resource);
	}

可以看出非常簡單,兩個構造方法,一個變數,資源載入的實現是this.reader.loadBeanDefinitions(resource)。

配置檔案處理

XmlBeanDefinitionReader載入資料就是在這裡,但是這裡有一個super(parentBeanFactory);一直往上跟會發現跟到了父類AbstractAutowireCapableBeanFactory。

	public AbstractAutowireCapableBeanFactory() {
		super();
		ignoreDependencyInterface(BeanNameAware.class);
		ignoreDependencyInterface(BeanFactoryAware.class);
		ignoreDependencyInterface(BeanClassLoaderAware.class);
	}

ignoreDependencyInterface:他會自動忽略給定介面的自動裝配功能。

Spring自動裝配:A依賴B,那麼當Spring獲取A的bean時後如果屬性B未初始化,那麼他就會自動初始化B;

有些情況下不能初始化B,比方說B實現了BeanNameAware介面;

  • Spring是這樣描述的:自動裝配時忽略給定的依賴介面,典型的應用是通過其他方式解析ApplicationContext註冊依賴,類似於BeanFactory通過BeanFactoryAware進行注入或者ApplicationContext通過ApplicationContextAware進行注入。

讀取Bean配置檔案

繼續描述this.reader.loadBeanDefinitions(resource)這個是資源載入的入口。

1.XmlBeanDefinitionReader首先對這個Resource物件用EncodedResource進行封裝。

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}

2.拿著Resource物件獲取輸入流,並構造inputSource

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();
}

3.通過構造的InputSource和Resource繼續呼叫函式doLoadBeanDefinitions。繼續往裡面跟進我們可以發現

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();
}

這裡的doLoadBeanDefinitions就是核心處理繼續跟進可以發現。

try {
	Document doc = doLoadDocument(inputSource, resource);
	return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
	throw ex;
}...

可以看到註冊Bean之前做了這些事情,

  1. 獲取xml驗證模式。
  2. 載入xml並獲取一個Document物件,
  3. 然後用這兩個物件去註冊Bean。

獲取XML驗證模式:

  • xml的驗證模式有DTD(document type definition)和XSD(xml schema definition)。

獲取Document:

  • 採用SAX解析XML,如果是dtd型別的檔案,解析器直接在當前路徑,如果是xsd的預設到META_INF/Spring.schemas資料夾裡找XSD檔案載入。

解析註冊BeanDifinitions:

通過解析配置獲取到Bean的定義之後開始註冊Bean。

驗證xml模式先不看了,繼續往下跟進,直接到註冊bean的地方。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
	// 例項化一個Reader
	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
	// 統計當前BeanDefinition的個數
	int countBefore = getRegistry().getBeanDefinitionCount();
	// 載入註冊Bean
	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
	// 返回本次註冊Bean的個數
	return getRegistry().getBeanDefinitionCount() - countBefore;
}

可以看到registerBeanDefinitions這裡就是載入註冊Bean的地方,繼續跟進下去。

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
	this.readerContext = readerContext;
	logger.debug("Loading bean definitions");
	Element root = doc.getDocumentElement();
	doRegisterBeanDefinitions(root);
}

doRegisterBeanDefinitions(root)這個就是重點了,繼續跟進去

protected void doRegisterBeanDefinitions(Element root) {
	// 預處理
	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)) {
				if (logger.isInfoEnabled()) {
					logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
							"] not matching: " + getReaderContext().getResource());
				}
				return;
			}
		}
	}
	// 解析前處理
	preProcessXml(root);
	// 處理
	parseBeanDefinitions(root, this.delegate);
	postProcessXml(root);
	this.delegate = parent;
}

但是繼續跟進去就發現很奇怪的現象。

protected void preProcessXml(Element root) {
}
protected void postProcessXml(Element root) {
}
這裡用到了模板方法模式。他是為子類所設計的方法,在Bean解析前後做一些處理,只需要繼承這個父類,子類自己去重寫就好了。

我們繼續往裡面跟進。

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);
	}
}

到這裡就發現一種是自定義名稱空間的處理,一種是預設的處理。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
		importBeanDefinitionResource(ele);
	}
	else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
		processAliasRegistration(ele);
	}
	else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
		processBeanDefinition(ele, delegate);
	}
	else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
		// recurse
		doRegisterBeanDefinitions(ele);
	}
}

我們走倒數第二個就是最普通的Bean id這種方式裝配


接下來進去發現最後容器是一個併發容器ConcurrentHashMap,這樣就把Bean放到了Spring容器中了,這就完成了Spring的Bean的註冊。