1. 程式人生 > >Spring原始碼深度解析(XmlBeanFactory原始碼解析上)

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原始碼深度解析(郝佳)