Spring原始碼深度解析(XmlBeanFactory原始碼解析上)
前言:
Spring容器有多種實現方式,一般來說可分為:BeanFactory和ApplicationContext
* BeanFactory提供了容器的基本功能,如getBean()等功能
* ApplicationContext介面繼承了BeanFactory,不但實現了BeanFactory的所有功能,還對其進行了擴充套件。
擴充套件功能如下:1)MessageSource,提供國際化的訊息訪問;2)資源訪問,如URL和檔案;3)事件傳播特性,即支援AOP特性;4)載入多個有繼承關係的上下文,使得每一個上下文都專注與一個特定的層次,比如應用的Web層
本文則基於BeanFactory介面的實現類XMLBeanFactory來介紹其載入xml檔案的過程
筆者使用SpringBoot來進行開發,spring-boot-start-parent版本為1.5.3.RELEASE,所依賴的Spring元件(如context、core)等版本為4.3.8.RELEASE
1.XMLBeanFactory的基本使用
1)建立實體類Student
@Data
public class Student {
private int id;
private String name;
}
2)建立檔案beans.xml,具體內容如下:
<?xml version="1.0" encoding="UTF-8"?> <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"> <bean id="student" class="test.Student"/> </beans>
3)測試方法
public class Test {
public static void main(String[] args) {
XmlBeanFactory bf = new XmlBeanFactory(new ClassPathResource("beans.xml"));
Student bean = (Student) bf.getBean("student");
System.out.println(bean); //結果為:Student(id=0, name=null)
}
}
以上便是通過載入xml的方式來獲取bean
2.寫在分析XmlBeanFactory原始碼之前
在檢視XmlBeanFactory實現其相關功能的原始碼以前,我們可以大膽猜想一下,如果是我們自己,那應該如何來實現這個功能?
如果是我的話,最簡單的思路就是:
1)先使用一個解析工具(一般來說就是DOM解析或者SAX解析)來解析beans.xml,獲取其內容(一般解析完成之後都是獲取一個Document)
2)解析該Document,組裝bean的基本資訊,如name、class等資訊,將這些資訊放到一個bean的實體之中
3)將組裝完的bean資訊,放到一個map中,name作為key,class對應的例項作為value,這樣使用者就可以通過getBean等方法來獲取該bean
大膽設想這種實現BeanFactory的方式,總體看來這樣實現簡單易用也是不錯的,讀者也可以自己設想下其實現方式。
3.XmlBeanFactory原始碼架構分析
下面跟隨筆者先大概看一下XmlBeanFactory載入bean.xml的大概架構
1)new XmlBeanFactory(Resource resource)構造XmlBeanFactory
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);// 自定義的Reader
public XmlBeanFactory(Resource resource) throws BeansException {// 預設構造方式
this(resource, null);// 呼叫下一個構造方法,parentBeanFactory預設為null
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource); // 主要功能實現
}
2)reader.loadBeanDefinitions(Resource resource) 解析器載入resource
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
...
try {
// 1.從resource中獲取流資訊
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 2.載入流資訊
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
...
}
3)doLoadBeanDefinitions(...)載入流資訊
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 1.通過xml解析inputStream來獲取xml對應的Document
Document doc = doLoadDocument(inputSource, resource);
// 2.解析Document,註冊其中的bean
return registerBeanDefinitions(doc, resource);
}
...
}
總結:通過以上對XmlBeanFactory結構的分析,可知,其主要功能也是按照我們的猜想來進行的
主要分為三個步驟:
* 將beans.xml載入為流資訊
* 解析該流資訊,將其解析為一個Document
* 載入該Document,註冊其中的bean(到某一個地方),該步驟也是最重要的步驟
下面按照這三個步驟,逐步解析XmlBeanFactory功能
4.將beans.xml載入為流資訊
由XMLBeanFactory的構造方法可知,構造器的入參為Resource,下面看一個Resource的主要方法:
/**
主要方法:
* @see #getInputStream()
* @see #getURL()
* @see #getURI()
* @see #getFile()
主要實現:
* @see WritableResource
* @see ContextResource
* @see UrlResource
* @see ClassPathResource
* @see FileSystemResource
* @see PathResource
* @see ByteArrayResource
* @see InputStreamResource
*/
public interface Resource extends InputStreamSource {}
主要實現方式有以上幾種,通常我們使用的就是ClassPathResource(將配置檔案放到src/main/resources中),或者FileSystemResource(將配置檔案放到系統盤某個路徑下)
在XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource)中可知
InputStream inputStream = encodedResource.getResource().getInputStream();
InputStream的獲取是通過Resource.getInputStream()方法來實現的,下面來檢視一下ClassPathResource.getInputStream()方法:
@Override
public InputStream getInputStream() throws IOException {
InputStream is;
// 預設情況下clazz為null
if (this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
}
// classLoader不為空,預設為ClassUtils.getDefaultClassLoader(),也就是Thread.currentThread().getContextClassLoader()
else if (this.classLoader != null) {
// 所以通過classLoader來載入path資源
is = this.classLoader.getResourceAsStream(this.path);
}
else {
is = ClassLoader.getSystemResourceAsStream(this.path);
}
if (is == null) {
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
}
return is;
}
總結:由上可知,bean.xml轉換為流資訊,主要是通過classLoader.getResourceAsStream()方法來實現的
5.解析InputStream,將其解析為一個Document
解析流為Document的主要程式碼為XmlBeanDefinitionReader.doLoadBeanDefinitions(InputSource inputSource, Resource resource)方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 1.通過xml解析inputStream來獲取xml對應的Document
Document doc = doLoadDocument(inputSource, resource);
...
}
...
}
下面來解析這個doLoadDocument方法
1)doLoadDocument()
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
2)loadDocument()
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() + "]");
}
// 1.建立DocumentBuilder,直接從DocumentBuilderFactory中獲取,通過factory.newDocumentBuilder()方法
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
// 2.使用DocumentBuilder來解析流資訊
return builder.parse(inputSource);
}
3)build.parse()
public Document parse(InputSource is) throws SAXException, IOException {
if (is == null) {
throw new IllegalArgumentException(
DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
"jaxp-null-input-source", null));
}
if (fSchemaValidator != null) {
if (fSchemaValidationManager != null) {
fSchemaValidationManager.reset();
fUnparsedEntityHandler.reset();
}
resetSchemaValidator();
}
// 通過DOM解析,來獲取Document
domParser.parse(is);
Document doc = domParser.getDocument();
domParser.dropDocumentReferences();
return doc;
}
總結:由上文可知,Spring使用了DOM解析的方式來解析InputStream,最終獲取Document
6.載入該Document,註冊其中的bean(到某一個地方)
下面來看XmlBeanDefinitionReader.doLoadBeanDefinitions(InputSource inputSource, Resource resource)方法的下半段
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
...
// 2.解析Document,註冊其中的bean
return registerBeanDefinitions(doc, resource);
}
...
}
1)XmlBeanDefinitionReader.registerBeanDefinitions()
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 1.建立BeanDefinitionDocumentReader,BeanDefinitionDocumentReader介面預設實現為DefaultBeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
// 2.註冊Document中的元素
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
2)DefaultBeanDefinitionDocumentReader.registerBeanDefinitions()
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
// 1.獲取rootElement
Element root = doc.getDocumentElement();
// 2.註冊
doRegisterBeanDefinitions(root);
}
// doRegisterBeanDefinitions()
protected void doRegisterBeanDefinitions(Element root) {
// 前置處理(暫時為空,使用者可自定義實現)
preProcessXml(root);
// 真正的解析處理
parseBeanDefinitions(root, this.delegate);
// 後置處理(暫時為空,使用者可自定義實現)
postProcessXml(root);
this.delegate = parent;
}
// parseBeanDefinitions()
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
// 1.獲取所有的子節點
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)) {
// 2.使用預設實現(本例中沒有自定義標籤,所以會使用該預設實現)
parseDefaultElement(ele, delegate);
}
else {
// 使用者自定義實現
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
// parseDefaultElement()
// 根據不同的nodeName,對應不同的解析方案,暫時只分析最重要的一個BEAN_ELEMENT的解析
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);
}
}
3)DefaultBeanDefinitionDocumentReader.processBeanDefinition(Elementele, BeanDefinitionParserDelegate delegate)最重要的方法
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 1.BeanDefinitionHolder用於全方位的來描述一個bean資訊,包括name/class/alias/等一系列屬性,將element解析為BeanDefinitionHolder
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 2.對其屬性進行裝飾
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 3.將bean例項註冊到(某一個地方)
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 4.傳送註冊完成事件給相應的監聽器
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
4)BeanDefinitionParserDelegate.parseBeanDefinitionElement()
將Document中的Element解析為一個BeanDefinitionHolder,BeanDefinitionHolder為一個bean資訊的綜合描述
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
// parseBeanDefinitionElement()
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
// 1.獲取id 和 name屬性值
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<String>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
// 2.判斷beanName是否重複,如果已有該beanName,則報錯
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 3.解析Element元素(重要方法)
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
// 4.如果使用者沒有寫id,則自動生成一個beanName
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
...
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
// 5.組裝為BeanDefinitionHolder
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
// parseBeanDefinitionElement()
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
// 1.獲取class屬性值
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
// 2.建立AbstractBeanDefinition,AbstractBeanDefinition為bean的描述資訊類(重要方法)
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 3.下面的parse方法均為對AbstractBeanDefinition中屬性的補充
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
...
return null;
}
// createBeanDefinition()
protected AbstractBeanDefinition createBeanDefinition(String className, String parentName)
throws ClassNotFoundException {
return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
}
// createBeanDefinition()
public static AbstractBeanDefinition createBeanDefinition(
String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
// 由className反射獲取BeanClass
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
總結:整個4)方法,總體來說就是為了獲取對bean的完整描述資訊,描述資訊都存放在BeanDefinitionHolder類中;BeanClass資訊由反射的方式來獲取
5)將BeanDefinitionHolder例項註冊到(某一個地方)
4)方法已經將Element元素解析為一個完整的BeanDefinitionHolder類,裡面包含了Element元素所有的基本資訊,下面就看下,如何註冊
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 主要就是這段程式碼
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));
}
}
// registerBeanDefinition()
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 1.獲取beanName
String beanName = definitionHolder.getBeanName();
// 2.進行註冊
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
...
}
// DefaultListableBeanFactory.registerBeanDefinition(),預設實現為DefaultListableBeanFactory
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
...
BeanDefinition oldBeanDefinition;
// 1.從當前的map中獲取,是否已有該bean,如果已有該bean,則重新覆蓋
// map為:private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
...
this.beanDefinitionMap.put(beanName, beanDefinition);
}
// 2.當前bean第一次載入
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
// 3.同步的情況下,將該bean放入map中
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
...
}
總結5):由以上分析可知,註冊行為就是將 beanName和beanDefinition 放入到DefaultListableBeanFactory的beanDefinitionMap中去
總結:
1)由6整個步驟可知:在獲取Document之後,註冊器所做的就是依次解析所有的Element,將Element解析為一個BeanDefinitionHolder,最後將beanName和對應的BeanDefinitionHolder放入到一個ConcurrentHashMap中去
2)到此為止,XMLBeanFactory的構造方法解析完畢,總結下其過程為:
* 將beans.xml檔案讀到記憶體,包裝為InputStream
* DOM解析的方式來解析InputStream,最後生成一個Document
* 註冊器解析Document,解析出Document的每一個Element
* 將Element解析為一個BeanDefinitionHolder
* 最後將beanName對應的BeanDefinitionHolder放入ConcurrentHashMap中,完成註冊步驟
參考:Spring原始碼深度解析(郝佳)