Spring源碼閱讀-BeanFactory初始化-配置加載

1、配置加載

每一個程序啓動都要加載配置,只是不一樣程序讀取配置方式不一樣,spring也有一套本身規則的配置方式,spring經過beanFactory來加載配置、管理對象,BeanFactory子類樹是很是複雜的,若是每個都看很是耗時間,能夠找一個典型的子類看一下它的初始化過程,以XmlBeanFactory爲例看spring加載配置。
看XmlBeanFactory的入口:java

private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
   super(parentBeanFactory);
   this.reader.loadBeanDefinitions(resource);
}

經過XmlBeanDefinitionReader 加載配置資源, 開始跟蹤loadBeanDefinitions的代碼,發現XmlBeanDefinitionReader 經過DefaultDocumentLoader對配置文件進行校驗並轉換成document,到這裏配置就算是驗證並加載到內存中了,下面就是經過BeanDefinitionDocumentReader解析doc標籤,看下面的代碼:web

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		//獲取根節點
		Element root = doc.getDocumentElement();
		//從根節點開始解析標籤
		doRegisterBeanDefinitions(root);
	}
	
	protected void doRegisterBeanDefinitions(Element root) {
		//獲取beans標籤的profile屬性
		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
		if (StringUtils.hasText(profileSpec)) {
			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec,
					BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			//看spring.profiles.active環境變量中是否有該屬性,若是沒有則不加載下面的標籤
			if (!getEnvironment().acceptsProfiles(specifiedProfiles)) {
				return;
			}
		}

		//標籤beans可能會存在遞歸的狀況, 每次都建立本身的解析器
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(this.readerContext, root, parent);

		//解析前處理、留給子類實現
		preProcessXml(root);
		//解析bean definition
		parseBeanDefinitions(root, this.delegate);
		//解析後處理、留給子類實現
		postProcessXml(root);

		this.delegate = parent;
	}
}

上面就是從根節點開始解析doc,這裏支持標籤beans嵌套的狀況,這樣方便咱們在發佈的時候改一個環境變量就能夠切換多套系統配置,好比以下配置:spring

<beans>
    <beans profile="dev">
    ...
    </beans>
    <beans profile="test">
    ...
    </beans>
</beans>
在web中使用,在web.xml配置環境變量
<context-param>
    <param-name>spring.profiles.active</param-name>
    <param-value>dev</param-value>
</context-param>數組

加載配置的時序圖以下:app

下面就是解析beans下面的標籤了,經過命名空間判斷是默認標籤仍是自定義標籤來走不一樣的流程。ide

2、默認標籤解析

分四種狀況import alais bean beans:post

  1. import: 解析出resource屬性獲取依賴配置資源用XmlBeanDefinitionReader遞歸加載配置
  2. alais:  把別名註冊到beanFactory中的aliasMap,須要判斷不能循環起別名,如:a->b,b->a
  3. beans:遞歸調用解析根節點的代碼doRegisterBeanDefinitions
  4. bean:用BeanDefinitionParserDelegate解析封裝成成BeanDefinitionHolder,把解析後的BeanDefinitionHolder註冊到beanFactory。到BeanDefinitionParserDelegate看BeanDefinitionHolder是怎麼建立的,先建立BeanDefinition,而後把該標籤下面的屬性、子標籤都set進BeanDefinition,最後把BeanDefinition、beanName和alias數組一塊兒封裝成BeanDefinitionHolder,看BeanDefinition的屬性基本能夠看出bean標籤下面能夠配置哪些屬性。

重點看一下解析bean標籤的時序圖:this

3、自定義標籤解析

自定義標籤在META-INF/spring.handlers定義的,經過標籤的命名空間獲取到對應的處理類handler,而後調用handler.parse方法,這裏主要看命名空間跟handler的對應關係是怎麼加載的。spa

看BeanDefinitionParserDelegate的parseCustomElement方法:debug

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
	String namespaceUri = getNamespaceURI(ele);
	//這個resolve方法裏面很重要, namespace的Handler是在這裏加載的
	NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
	if (handler == null) {
		error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
		return null;
	}
	return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

到resolve中看是怎麼獲取的NamespaceHandler:

public NamespaceHandler resolve(String namespaceUri) {
	//加載全部handlers配置
	Map<String, Object> handlerMappings = getHandlerMappings();
	//獲取到對應handler的handler對象或者字符串
	Object handlerOrClassName = handlerMappings.get(namespaceUri);
	if (handlerOrClassName == null) {
		return null;
	} else if (handlerOrClassName instanceof NamespaceHandler) {
		//若是已經初始化過直接返回
		return (NamespaceHandler) handlerOrClassName;
	} else {
		//未初始化的經過反射實例化對象,而且調用init方法,init方法是把標籤跟對應的parser作映射
		String className = (String) handlerOrClassName;
		try {
			Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
			if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
				throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri
						+ "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
			}
			NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
			namespaceHandler.init();
			handlerMappings.put(namespaceUri, namespaceHandler);
			return namespaceHandler;
		} catch (ClassNotFoundException ex) {
			throw new FatalBeanException(
					"NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found",
					ex);
		} catch (LinkageError err) {
			throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace ["
					+ namespaceUri + "]: problem with handler class file or dependent class", err);
		}
	}
}
private Map<String, Object> getHandlerMappings() {
	if (this.handlerMappings == null) {
		synchronized (this) {
			if (this.handlerMappings == null) {
				try {
					//把properties文件加載到map中
					Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation,
							this.classLoader);
					if (logger.isDebugEnabled()) {
						logger.debug("Loaded NamespaceHandler mappings: " + mappings);
					}
					Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
					CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
					this.handlerMappings = handlerMappings;
				} catch (IOException ex) {
					throw new IllegalStateException("Unable to load NamespaceHandler mappings from location ["
							+ this.handlerMappingsLocation + "]", ex);
				}
			}
		}
	}
	return this.handlerMappings;
}

選一個NamespaceHandler看看它的init方法是作什麼的:

public class AopNamespaceHandler extends NamespaceHandlerSupport {

	/**
	 * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the '
	 * {@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
	 * and '{@code scoped-proxy}' tags.
	 */
	@Override
	public void init() {
		// In 2.0 XSD as well as in 2.1 XSD.
		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
		registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

		// Only in 2.0 XSD: moved to context namespace as of 2.1
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	}
}

public abstract class NamespaceHandlerSupport implements NamespaceHandler {
	protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
		//元素解析器放入parsers 集合中:
		this.parsers.put(elementName, parser);
	}
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		//這裏調用BeanDefinitionParser的parse方法
		return findParserForElement(element, parserContext).parse(element, parserContext);
	}
	//獲取BeanDefinitionParser
	private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
		String localName = parserContext.getDelegate().getLocalName(element);
		//從前面加載的集合中根據標籤名稱獲取Parser
		BeanDefinitionParser parser = this.parsers.get(localName);
		if (parser == null) {
			parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionParser for element [" + localName + "]",
					element);
		}
		return parser;
	}
}

到這裏NamespaceHandler就獲取到了,迴歸到上面parseCustomElement方法,handler調用parse方法,parse方法裏面首先根據標籤到parsers集合裏面找到對應的解析器並調用對應解析器的parse方法,時序圖:

相關文章
相關標籤/搜索