spring-mvc源碼-bean定義加載

接着上篇,在根上下文初始化的過程中,有一步配置和啓動根上下文方法:org.springframework.web.context.ContextLoader#configureAndRefreshWebApplicationContext,這裏麪包含了對bean的所有處理,下面我們慢慢來看。

先看下這個方法的實現:configureAndRefreshWebApplicationContext

	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
			if (idParam != null) {
				wac.setId(idParam);
			}
			else {
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(sc.getContextPath()));
			}
		}

		wac.setServletContext(sc);
		//讀取web.xm中配設置的contextConfigLocation參數值
		String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
		if (configLocationParam != null) {
			wac.setConfigLocation(configLocationParam);
		}

		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
		}

		customizeContext(sc, wac);
		//根上下文(ioc容器)的啓動,bean的處理也在這裏
		wac.refresh();
	}

然後進入refresh()方法:

	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			//準備啓動上下文,設置開始時間,標記活動標誌,初始化配置文件中的佔位符
			prepareRefresh();

			//將 bean 定義加載到給定的 BeanFactory 中
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			//準備 BeanFactory 以便在此上下文中使用。
			// 1. 設置 BeanFactory 的類加載器
			// 2. 添加幾個 BeanPostProcessor
			// 3. 實例化幾個特殊的 bean
			prepareBeanFactory(beanFactory);

			try {
				//爲空實現,留給子類做擴展,不同 ApplicationContext 實現不同
				postProcessBeanFactory(beanFactory);

				// Spring 的 SPI
				// 先調用 BeanDefinitionRegistryPostProcessor 和 ImportBeanDefinitionRegistrar 的實現類
				// 再調用 BeanFactoryPostProcessor 各個實現類的 postProcessBeanFactory(factory) 方法
				// 例如:ConfigurationClassPostProcessor 會掃描 <context:component-scan/> 和 @SpringBootApplication(scanBasePackages = "") 中的Component,並且將 @Configuration 類中的 @Bean register 到 BeanFactory 中
				// 擴展例如:MyBatis MapperScannerConfigurer 和 MapperScannerRegistrar,掃描Mapper register 到 BeanFactory 中
				invokeBeanFactoryPostProcessors(beanFactory);

				// 註冊 BeanPostProcessor 的實現類,不同於剛剛的 BeanFactoryPostProcessor
				// BeanPostProcessor 接口兩個方法 postProcessBeforeInitialization 和 postProcessAfterInitialization 會在 Bean 初始化之前和之後調用
				// 這邊 Bean 還沒初始化,下面的 finishBeanFactoryInitialization 纔是真正的初始化方法
				registerBeanPostProcessors(beanFactory);

				// 初始化當前 ApplicationContext 的 MessageSource,解析消息的策略接口,用於支持消息的國際化和參數化
				// Spring 兩個開箱即用的實現 ResourceBundleMessageSource 和 ReloadableResourceBundleMessageSource
				initMessageSource();

				// 初始化當前 ApplicationContext 的事件廣播器
				initApplicationEventMulticaster();

				// 典型模板方法
				// 子類可以在實例化 bean 之前,做一些初始化工作,SpringBoot 會在這邊啓動 Web 服務
				onRefresh();

				// 向 initApplicationEventMulticaster() 初始化的 applicationEventMulticaster 註冊事件監聽器,就是實現 ApplicationListener 接口類
				// 觀察者模式,例如實現了 ApplicationEvent,通過 ApplicationEventPublisher#publishEvent(),可以通知到各個 ApplicationListener#onApplicationEvent
				registerListeners();

				// 初始化所有的 singletons bean(lazy-init 的除外)
				// Spring bean 初始化核心方法
				finishBeanFactoryInitialization(beanFactory);

				// 初始化完成(ContextRefreshedEvent)事件
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// destroy 已經創建的 singleton 避免佔用資源
				destroyBeans();

				// 重置'active'標誌
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				resetCommonCaches();
			}
		}
	}

進入obtainFreshBeanFactory()方法,這個方法裏面調用了refreshBeanFactory()和getBeanFactory()方法,這兩個方法都是在子類AbstractRefreshableApplicationContext中實現的,主要看refreshBeanFactory()方法,如下:

	@Override
	protected final void refreshBeanFactory() throws BeansException {
		//判斷當前ApplicationContext是否已經有 BeanFactory ,如果有,銷燬所有 Bean,關閉 BeanFactory
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			//創建一個可以獨立使用的容器
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			//設置 BeanFactory 的兩個配置屬性:是否允許 Bean 覆蓋、是否允許循環引用
			customizeBeanFactory(beanFactory);
			//解析配置文件,並將bean的信息註冊到容器
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

loadBeanDefinitions()在子類XmlWebApplicationContext實現,代碼如下:

	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		//創建XmlBeanDefinitionReader對象,並將beanFactory綁定,XmlBeanDefinitionReader是BeanDefinitionReader的一個實現類,負責對xml的配置文件進行讀取
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		//設置環境,這裏的環境是StandardServletEnvironment,裏面主要有五項屬性:servletConfigInitParams、servletContextInitParams、jndiProperties、systemProperties、systemEnvironment
		//這個StandardServletEnvironment創建比較早,在爲根上下文設置configLocationParam的時候創建的
		beanDefinitionReader.setEnvironment(getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		//ResourceEntityResolver包含BeansDtdResolver和PluggableSchemaResolver,用以在classpath下搜尋schema和DTD文件
		//PluggableSchemaResolver有一個schemaMappingsLocation屬性其值爲寫死的META-INF/spring.schemas
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		//允許子類提供reader的自定義初始化,然後繼續實際bean定義加載。這裏是空實現
		initBeanDefinitionReader(beanDefinitionReader);
		//加載bean的定義
		loadBeanDefinitions(beanDefinitionReader);
	}

繼續看loadBeanDefinitions()方法,如下:

	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
		//這裏獲取到的配置文件,就是在web.xml裏面配置的contextConfigLocation,一般是:classpath:spring-config.xml
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			for (String configLocation : configLocations) {
				//解析配置文件,加載bean的定義
				reader.loadBeanDefinitions(configLocation);
			}
		}
	}

調用XmlBeanDefinitionReader父類AbstractBeanDefinitionReader的loadBeanDefinitions(String location)方法,這個方法直接調用的loadBeanDefinitions(String location, Set<Resource> actualResources)方法,這個解析過程都在這裏,如下:

	public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
		//獲取ResourceLoader,此處的ResourceLoader就是我們創建的XmlWebApplicationContext
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
		}

		//判斷XmlWebApplicationContext是否爲ResourcePatternResolver,通過類繼承結構可以看出,XmlWebApplicationContext是ResourcePatternResolver的子類,所以會進入第一個條件分支
		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
				//獲取resources,這裏的resources裏面放的就是spring的xml配置文件信息
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				//調用loadBeanDefinitions(Resource... resources)解析配置文件,並返回beanDefinition的數量
				int loadCount = loadBeanDefinitions(resources);
				//加載過程中已經被解析過的實際的Resource的填充集合
				if (actualResources != null) {
					for (Resource resource : resources) {
						actualResources.add(resource);
					}
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
				}
				return loadCount;
			}
			catch (IOException ex) {
				throw new BeanDefinitionStoreException(
						"Could not resolve bean definition resource pattern [" + location + "]", ex);
			}
		}
		else {
			//只能通過絕對URL加載單個資源
			Resource resource = resourceLoader.getResource(location);
			int loadCount = loadBeanDefinitions(resource);
			if (actualResources != null) {
				actualResources.add(resource);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
			}
			return loadCount;
		}
	}

說明下爲什麼要判斷當前resourceLoader是否是ResourcePatternResolver類型的,因爲ResourceLoader只是提供了對classpath前綴的支持。而ResourcePatternResolver提供了對classpath*前綴的支持。也就是說ResourceLoader提供classpath下單資源文件的載入,而ResourcePatternResolver提供多資源文件的載入。

調用loadBeanDefinitions(Resource... resources)解析配置文件,這個方法就是循環解析所有的配置文件,具體的解析,調用子類XmlBeanDefinitionReader的loadBeanDefinitions(Resource resource)這個方法,這個方法直接調用loadBeanDefinitions(EncodedResource encodedResource)這個方法,這個方法代碼如下:

	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isInfoEnabled()) {
			logger.info("Loading XML bean definitions from " + encodedResource.getResource());
		}

		//檢查是否重複加載xml配置
		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<EncodedResource>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		try {
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				//真正的開始配置文件解析
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
				inputStream.close();
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"IOException parsing XML document from " + encodedResource.getResource(), ex);
		}
		finally {
			currentResources.remove(encodedResource);
			if (currentResources.isEmpty()) {
				this.resourcesCurrentlyBeingLoaded.remove();
			}
		}
	}

在Spring代碼中,最後真正執行操作的方法一般都是doXXXX,我們在工作中寫代碼也可以參考這種定義。

	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			//將xml文件轉成標準的Document對象,採用SAX的方式解析
			Document doc = doLoadDocument(inputSource, resource);
			//載入並註冊
			return registerBeanDefinitions(doc, resource);
		}
	}

繼續看org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions這個方法:

	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		//這裏的documentReader使用的是DefaultBeanDefinitionDocumentReader
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		//記錄統計前的BeanDefinition數
		int countBefore = getRegistry().getBeanDefinitionCount();
		//加載並註冊
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		//返回本次加載的BeanDefinition數
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

繼續看org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#registerBeanDefinitions這個方法:

	@Override
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		Element root = doc.getDocumentElement();
		//真正的註冊bean定義
		doRegisterBeanDefinitions(root);
	}

接着看org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions這個方法:

	protected void doRegisterBeanDefinitions(Element root) {
		BeanDefinitionParserDelegate parent = this.delegate;
		//創建 BeanDefinitionParserDelegate 對象,這個對象的作用是將 Document 的內容轉成 BeanDefinition 實例
		this.delegate = createDelegate(getReaderContext(), root, parent);

		//驗證 XML 文件的命名空間,即判斷是否含有 xmlns="http://www.springframework.org/schema/beans"
		if (this.delegate.isDefaultNamespace(root)) {
			//取得 beans 標籤中 profile 的屬性內容,該標籤主要用於環境的切換。例如開發過程中,一般存在測試環境和正式環境,兩者之間可能存在不同的數據源。若想要實現環境的快速切換,就可以利用 profile 來配置。
			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;
				}
			}
		}

		//前置處理(空實現,供子類實現,方便擴展)
		preProcessXml(root);
		//開始解析 Bean 定義
		parseBeanDefinitions(root, this.delegate);
		//後置處理(空實現,供子類實現,方便擴展)
		postProcessXml(root);

		this.delegate = parent;
	}

繼續看org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions這個方法:

	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		//驗證 XML 文件的命名空間,即判斷是否含有 xmlns="http://www.springframework.org/schema/beans"
		if (delegate.isDefaultNamespace(root)) {
			//取得 <beans> 的所有子節點
			NodeList nl = root.getChildNodes();
			//遍歷 <beans> 的子節點
			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)) {
						//解析 <beans> 子節點的內容
						parseDefaultElement(ele, delegate);
					}
					else {
						//非xmlns="http://www.springframework.org/schema/beans"這個命名空間的標籤解析,例如:<context:annotation-config/>
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			//非xmlns="http://www.springframework.org/schema/beans"這個命名空間的標籤解析,例如:<context:annotation-config/>
			delegate.parseCustomElement(root);
		}
	}

這裏可以看到,bean的定義解析分成了兩個分支,一個是bean標籤的解析,一個是非bean標籤及自定義標籤的解析,這塊內容也很多,下篇繼續。

 

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