我們的日常開發幾乎離不開Spring,他為我們的開發帶來了很大的便捷,那么Spring框架是如何做到方便他人的呢。今天就來說說bean如何被加載加載。
我們在xml文件中寫過太多類似這樣的bean聲明
<bean id="jsonParser" class="com.jackie.json.FastjsonJsonParser" />
那么這樣的聲明是如果被Spring讀取并加載的呢?
DefaultListableBeanFactory
繼承關系
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable
上面是DefaultListableBeanFactory類源碼的聲明,集成了父類AbstractAutowireCapableBeanFactory以及實現了接口
ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable(且不管這些又長有難懂的父類和父接口都是干什么的)
上面是DefaultListableBeanFactory的所有依賴父類,可以看到最頂層使我們熟悉的Object類。
地位
DefaultListableBeanFactory類是整個bean加載的核心部分。后面我們要說到的XmlBeanFactory就是DefaultListableBeanFactory的子類。
XmlBeanFactory
XmlBeanFactory是DefaultListableBeanFactory的子類。其與DefaultListableBeanFactory類的不同之處通過看源碼就會發現,XmlBeanFactory使用了自定義的XmlBeanDefinitionReader用于讀取xml文件內容。
XmlBeanFactory的源碼并不復雜
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
/**
* Create a new XmlBeanFactory with the given resource,
* which must be parsable using DOM.
* @param resource XML resource to load bean definitions from
* @throws BeansException in case of loading or parsing errors
*/
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
/**
* Create a new XmlBeanFactory with the given input stream,
* which must be parsable using DOM.
* @param resource XML resource to load bean definitions from
* @param parentBeanFactory parent bean factory
* @throws BeansException in case of loading or parsing errors
*/
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
}
- 有兩個構造函數
- 兩個構造函數都有參數Resource,我們在調用前可以通過各種實現比如
new ClassPathResource("application-context.xml")
的方式傳值 - Resource有很多實現比如 文件(FileSystemResource)、URL資源(UrlResource)、InputStream資源(InputStreamResource)、Byte數組(ByteArrayResource)等
- 使用Resource是因為該接口抽象出了Spring用到的各種底層資源比如File、Url、Classpath等
bean加載過程
說到這里的bean加載就離不開我們上面提到的XmlBeanFactory類。
一切的加載XML以及解析bean的操作都要從XmlBeanFactory的
XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)
說起。
步驟分析
理所當然,我們首先點擊進入XmlBeanDefinitionReader類的loadBeanDefinitions方法。整個過程簡單說就是:
1.首先對傳入的Resouce進行封裝,成為EncodeResource對象(之所以這么做,是考慮可以對于資源文件進行編碼)。Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isInfoEnabled()) { logger.info("Loading XML bean definitions from " + encodedResource.getResource()); }
得到Resource的輸入流
InputStream inputStream = encodedResource.getResource().getInputStream();
3.調用doLoadBeanDefinitions方法
進入doLoadBeanDefinitions方法,我們可以看到方法實現也很簡單
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { int validationMode = getValidationModeForResource(resource); Document doc = this.documentLoader.loadDocument( inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); return registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); } catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); } catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); } catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); } catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } }
主要做了三件事
得到XML文件的驗證模式
int validationMode = getValidationModeForResource(resource);
2.加載XML文件,得到對應的Document對象(這里可以發現是調用了this.documentLoader即DefaultDocmentLoader類)
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
這里的解析成Document其實就是常規的SAX解析XML的操作。
3.根據得到的Document對象注冊Bean
registerBeanDefinitions(doc, resource)
解析注冊BeanDefinitions
上面的在得到Docment對象后,就是需要解析注冊BeanDefinitions了。
進入registerBeanDefinitions方法我們可以看到其實現為
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
documentReader.setEnvironment(this.getEnvironment());
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
我們繼續深入,則進入到類DefaultBeanDefinitionDocumentReaderregisterBeanDefinitions方法的doRegisterBeanDefinitions實現。
在這個方法里實現了對于XML中的bean真正的解析
protected void doRegisterBeanDefinitions(Element root) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!this.environment.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 = createHelper(this.readerContext, root, parent);
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
追蹤該方法中的每一個方法就是具體執行解析xml并加載bean的過程,有興趣可以打斷點調試一遍。
如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”將是我最大的寫作動力!如果您想持續關注我的文章,請掃描二維碼,關注JackieZheng的微信公眾號,我會將我的文章推送給您,并和您一起分享我日常閱讀過的優質文章。
文章列表
留言列表