spring源碼obtainFreshBeanFactory方法,bean標籤解析

1,說明

spring 的源碼包括了很龐大的知識,我們通常說的spring指的是spring framework, 裏面包含了AOP,容器等等,本章開始講解xml的解析,

先看測試用例,使用 spring-5.0.15 版本:

public class MainTest {

	public static void main(String[] args) {
        // 傳入配置文件,獲取 ApplicationContext 
		ClassPathXmlApplicationContext ioc = new 
        ClassPathXmlApplicationContext("classpath:spring.xml");
        // 通過getBean 獲取bean
		System.out.println(ioc.getBean("car"));
}

spring.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"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xmlns:task="http://www.springframework.org/schema/task" xmlns:tx="http://www.springframework.org/schema/tx"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

	<context:component-scan base-package="com.mystudy">
	</context:component-scan>
    <bean id = "xmlBean" class="com.mystudy.entity.XmlBean"></bean>

</beans>

2,方法預覽

1,查看  ClassPathXmlApplicationContext 源碼,類構造方法


public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
    // 省略其他方法,
    // 調用構造方法,並調用字方法
    public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}
    //  主要看refresh 方法
    public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {
		/**
		 * 初始化,父類各個資源,
		 */
		super(parent);
		//格式化資源路徑中特殊字符,
		setConfigLocations(configLocations);
		if (refresh) {
			//整個spring 比較核心的方法,
			refresh();
		}
	}

}

2,點開refresh方法,  裏面有9個方法,可以看一個大概的意思,前面幾個方法着重是初始化資源文件,做一些校驗,finishBeanFactoryInitialization纔是真正初始化bean

這裏需要強調一點,java bean 和spring bean 有區別, 只要new一個對象都可以稱爲java bean但不是spring bean。spring bean從bean實例化,屬性的賦值,到初始化,一個完整的生命週期,

其中可以着重看的方法 1,obtainFreshBeanFactory (獲取 BeanFactory),2,finishBeanFactoryInitialization(實例化單例bean, 後面單獨寫一篇)

    @Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			// 前期準備工作, 初始化一些變量,
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			//初始化 beanFactory,
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			// 初始化 bean factory 需要用到的上下文
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				// 預留用戶自定義方法
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				// bean工廠後置處理器,修改bean工廠裏面的值
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				//bean的後置處理器,干預bean的創建
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				// 國際化語言
				initMessageSource();

				// Initialize event multicaster for this context.
				// 創建事件的監聽器
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				//留給子類實現
				onRefresh();

				// Check for listener beans and register them.
				// 把時間監聽器註冊到監聽器上
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				// 實例化剩餘的單利bean
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				// 容器刷新,發佈容器刷新時間(spring cloud 也是在這裏啓動)
				finishRefresh();
			} catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

3,obtainFreshBeanFactory 方法講解

點進 obtainFreshBeanFactory方法,可以看到主要乾了兩件事 :

  • 調用子類方法,
  • 獲取子類方法初始化的BeanFactory 

而子類方法 refreshBeanFactory 

  • 初始化了一個 DefaultListableBeanFactory對象,
  • 調用 AbstractXmlApplicationContext#loadBeanDefinitions方法,初始化一個XmlBeanDefinitionReader對象,並初始化環境變量和資源文件,調用本類方法loadBeanDefinitions,獲取資源文件路徑,並開始解析
// 去掉其他沒有調用的方法
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		// 加載並初始化 beanFacotry,進入子方法
		refreshBeanFactory();
		//獲取上面初始化的BeanFactory
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
		}
		return beanFactory;
	}

protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			// 如果beanFactory已經被初始化,銷燬
			destroyBeans();
			closeBeanFactory();
		}
		try {
			// 初始化一個 DefaultListAbleBeanFactory
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
			//加載beanDefinition
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

往下走會到 AbstractBeanDefinitionReader#loadBeanDefinitions (String location, @Nullable Set<Resource> actualResources) 方法

  • 將資源文件轉化爲Resource
  • XmlBeanDefinitionReader#loadBeanDefinitions 方法 將Resource 轉成InputStream
  • 進入子方法 doLoadBeanDefinitions 將InputStream 轉Document
  • 創建對象BeanDefinitionDocumentReader對象,調用registerBeanDefinitions方法, 獲取root節點,並開始真正解析,調用doRegisterBeanDefinitions方法
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;
				}
			}
		}

		//預留前置解析xml接口, 用戶可自定義實現
		preProcessXml(root);
		// 開始解析 標籤
		parseBeanDefinitions(root, this.delegate);
		//預留後置解析xml接口, 用戶可自定義實現
		postProcessXml(root);

		this.delegate = parent;
	}

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		//解析整個xml,首先解析最外層document對象, 對應前面XML文件 Beans標籤
		//判斷是否是系統標籤
		if (delegate.isDefaultNamespace(root)) {
			// 獲取第二層系統標籤  對應前面XML文件, beans和context:component-scan
			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 {
			// 用戶自定義Xml
			delegate.parseCustomElement(root);
		}
	}

4,標籤解析

1,系統標籤解析

可以看到,系統標籤 包含,import, alias, bean, beans,看看bean標籤主要做的事

  • 解析id, name 確定BeanDefinition 的name
  • 解析 className, parent, 創建AbstractBeanDefinition對象,真實創建的是GenericBeanDefinition對象
  • 解析標籤裏面各個屬性值,保存到GenericBeanDefinition 對象,包括,是否單例,抽象,懶加載,作用域,依賴,是否私有等等,方便後面在初始化bean的時候用到
	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			// import 標籤
			importBeanDefinitionResource(ele);
		}
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			// alias 標籤
			processAliasRegistration(ele);
		}
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			// bean 標籤
			processBeanDefinition(ele, delegate);
		}
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// beans 標籤
			doRegisterBeanDefinitions(ele);
		}
	}
// 子方法
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	// 解析各個屬性
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    //省略代碼
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
	return parseBeanDefinitionElement(ele, null);
}

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
		// 解析屬性值 id,name
		String id = ele.getAttribute(ID_ATTRIBUTE);
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

		// 將name,存儲別名list中
		List<String> aliases = new ArrayList<>();
		if (StringUtils.hasLength(nameAttr)) {
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			aliases.addAll(Arrays.asList(nameArr));
		}

		String beanName = id;
		// 如果id,name 同時存在,優先取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");
			}
		}

		// 傳的值是null,主要用來檢測beanName的唯一性,是否和其他beanName重名
		if (containingBean == null) {
			checkNameUniqueness(beanName, aliases, ele);
		}

		// 創建AbstractBeanDefinition對象,並解析class ,parent等等,各種屬性值,
		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		// 省略代碼
}    

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章