導讀:
XmlBeanFactory繼承自DefaultListableBeanFactory,擴充了從xml文檔中讀取bean definition的能力。從本質上講,XmlBeanFactory等同于DefaultListableBeanFactory+XmlBeanDefinitionReader ,如果有更好的需求,可以考慮使用DefaultListableBeanFactory+XmlBeanDefinitionReader方案,因為該方案可以從多個xml檔案讀取資源,并且在解析xml上具有更靈活的可配置性。
1 XmlBeanFactory的使用
典型構造方法:
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
其中,
reader是XmlBeanDefinitionReader的執行個體,XmlBeanDefinitionReader繼承自AbstractBeanDefinitionReader(http://www.cnblogs.com/davidwang456/p/4190428.html 已經介紹過)。
Resource 接口封裝了各種可能的資源類型,也就是對使用者來說屏蔽了檔案類型的不同。這樣所有的資源都被可以通過 InputStream 這個類來擷取,是以也屏蔽了資源的提供者。另外還有一個問題就是加載資源的問題,也就是資源的加載者要統一,從上圖中可以看出這個任務是由 ResourceLoader 接口完成,他屏蔽了所有的資源加載者的差異,隻需要實作這個接口就可以加載所有的資源,他的預設實作是 DefaultResourceLoader。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiInBnaugjM2cDOxIDNwkDN5ADOw8CXxATNxAjMvwFN3AjN4QzLcd2bsJ2Lc12bj5ycn9Gbi52YuAzcldWYtl2Lc9CX6MHc0RHaiojIsJye.jpg)
1.1 Resource 接口繼承了 InputStreamSource 接口,這個接口中有個 getInputStream 方法,傳回的是 InputStream 類。
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
getInputStream設計希望每次請求都傳回一個新的流。這種要求在一個場合非常重要,例如javamail api,它在建立多個郵件附件時需要多次讀取到流,在這中情況下,它就要求每個getInputStream請求能傳回一個新的流。
1.2 抽象資源類AbstractResource繼承自Source接口,實作了部分方法。
exists方法檢查一個檔案或者輸入量是否可以打開;
isOpen 通常傳回false;
getUrl和getFile 抛出異常;
toString傳回描述資訊。
另外有一個有用的方法:lastModified()傳回檔案的最後修改時間。調用了File.lastModified() 傳回此抽象路徑名表示的檔案最後一次被修改的時間。
@Override
public long lastModified() throws IOException {
long lastModified = getFileForLastModifiedCheck().lastModified();
if (lastModified == 0L) {
throw new FileNotFoundException(getDescription() +
" cannot be resolved in the file system for resolving its last-modified timestamp");
}
return lastModified;
}
1.3 Resource的實作子類:
AbstractResource的直接子類有:AbstractFileResolvingResource, ByteArrayResource,BeanDefinitionResource, DescriptiveResource, FileSystemResource, InputStreamResource, PathResource, VfsResource。
其中,AbstractFileResolvingResource的直接子類有:ClassPathResource, PortletContextResource, ServletContextResource, UrlResource
比較重要的實作類為實作了ContextResource接口的資源類,ContextResource接口繼承了Resource接口,通過從一個封裝的context中加載資源來擴充Resource接口的功能。例如:可以從ServletContext,PortletContext,普通的classpth路徑或者檔案系統的相對路徑(特别是沒有指明字首,是以适用于本地ResourceLoader的context),其子類實作有:PortletContextResource, ServletContextResource,ClassPathContextResource,ClassRelativeContextResource,FileSystemContextResource.
1.4 資源的加載ResourceLoader,ResourceLoader是一個加載資源的政策接口,可以從classpth或者檔案系統中,ApplicationContext需要加載資源功能,使用了其擴充類ResourcePatternReolver。DefaultResourceLoader是該接口的标準實作,适用于一個ApplicationContext外部,可以用在ResourceEditor中。在application允許過程中,使用特定的context子原因加載政策,可以将type資源和資源組通過string的形式設定bean的屬性。
(來源:http://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/)
2. Bean的解析與注冊詳細過程分析
(來源:http://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/)
首先, XmlBeanFactory構造函數this.reader.loadBeanDefinitions(resource);
1. XmlBeanDefinitionReader加載bean定義:loadBeanDefinitions方法
2. XmlBeanDefinitionReader解析bean定義,并注冊:doLoadBeanDefinitions方法
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
3.1. 使用标準的JAXP配置的xml解析器從Resource中加載到Document。
/**
* Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
* XML parser.
*/
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
3.2.根據加載的Document 注冊Bean definition。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
documentReader.setEnvironment(getEnvironment());
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
3.2.1 建立BeanDefinitionDocument的解析器BeanUtils.instantiateClass(this.documentReaderClass)
3.2.2 BeanDefinitionDocument解析器注冊bean definition,從根節點<beans>開始
/**
* Register each bean definition within the given root {@code <beans/>} element.
*/
protected void doRegisterBeanDefinitions(Element root) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}
}
// 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(this.readerContext, root, parent);
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);
}
}
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);
}
}
4. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));--->
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
5.調用:
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
}
6.BeanDefinitionReaderUtils.registerBeanDefinition
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
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));
}
}
7.處理給定的Bean元素,并解析到BeanDefinition。
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
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));
}
}
8.
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String aliase : aliases) {
registry.registerAlias(beanName, aliase);
}
}
}
9.将解析的Bean定義放入BeanDefinitionNames和BeanDefinitionMap儲存已經aliasMap等
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition oldBeanDefinition;
synchronized (this.beanDefinitionMap) {
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
" with a framework-generated bean definition ': replacing [" +
oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
else {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
}
else {
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
3. 一個非常重要的部分就是建立 Bean 對象執行個體之間的關系,這也是 Spring 架構的核心競争力,何時、如何建立他們之間的關系請看下面的時序圖:
來源:http://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/
4. 小結:
從spring 3.1版本後,xmlBeanFactory被表明為Deprecated. 推薦使用DefaultListableBeanFactory和XmlBeanDefinitionReader替換。
本文從xmlBeanFactory擴充到XmlBeanDefinitionReader,再到Resource接口。
一些情節還沒有理順,顯得有些雜亂,後續會修改(寫的很累,休整一下)。寫這篇部落格的時候,這句話一直在我腦邊回想:
"寫書就像冒險。一開始它是玩具和娛樂。然後她成了你的情婦。然後是你的主人。然後變成一個暴君。最後你終于認命的時候,它死了,然後給拖到外面遊街。" ----溫斯頓.丘吉爾
參考文獻:
1. http://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/
轉載于:https://www.cnblogs.com/davidwang456/p/4204569.html