Spring原始碼解析之三(自定義標籤的解析)
自定義標籤的解析
1、概述:通過前面的文章我們分析了預設標籤的解析,我們先回顧下自定義標籤解析所使用的方法
/** * Parse the elements at the root level in the document: * "import", "alias", "bean". * @param root the DOM root element of the document */ protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { 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 { delegate.parseCustomElement(root); } }
其中delegate.parseCustomElement(root);就是自定義標籤解析的方法,本篇文章主要圍繞此篇文章展開。
2、自定義標籤的解析
public BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null); } //containingBd為父類的bean,對頂層元素的解析應該設定為null public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { //獲取相應的名稱空間 String namespaceUri = getNamespaceURI(ele); NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } //呼叫自定義的NamespaceHandler進行解析 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }
主要思路就是根據對應的bean獲取對應的名稱空間,根據名稱空間解析對應的處理器,然後根據使用者自定義的處理器進行解析。
3、獲取標籤的名稱空間、String namespaceUri = getNamespaceURI(ele);
4、提取自定義標籤處理器、NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
public final NamespaceHandlerResolver getNamespaceHandlerResolver() { return this.namespaceHandlerResolver; } 實際是public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver 中的resolve的方法 @Override public NamespaceHandler resolve(String namespaceUri) { //獲取所有已經配置的handler對映 Map<String, Object> handlerMappings = getHandlerMappings(); //根據名稱空間找到相對應的資訊 Object handlerOrClassName = handlerMappings.get(namespaceUri); if (handlerOrClassName == null) { return null; } else if (handlerOrClassName instanceof NamespaceHandler) { //已經解析過的,直接從快取讀取 return (NamespaceHandler) handlerOrClassName; } else { 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的初始化方法 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); } } }
程式碼中的流程已經很清楚,這裡面的程式碼namespaceHandler.init()其實就是用到我們自定義的handler的init方法。我們具體看下getHandlerMappings()方法,Map<String, Object> handlerMappings = getHandlerMappings(); 進入到函式體:
/**
* Load the specified NamespaceHandler mappings lazily.
*/
private Map<String, Object> getHandlerMappings() {
//如果沒有被快取,則開始被快取
if (this.handlerMappings == null) {
synchronized (this) {
if (this.handlerMappings == null) {
try {
//this.handlerMappingsLocation在建構函式中已經被初始化為:META-INF/Spring.handlers
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());
//將properties格式檔案合併到Map格式的handlerMappings中
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;
}
5、標籤的解析、return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
委託給自定義的解析器去解析。此時的handler已經被例項化為我們自定義的了。
* Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is
* registered for that {@link Element}.
*/
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
//尋找解析器並進行解析操作
return findParserForElement(element, parserContext).parse(element, parserContext);
}
/**
* Locates the {@link BeanDefinitionParser} from the register implementations using
* the local name of the supplied {@link Element}.
*/
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
//獲取元素名稱
String localName = parserContext.getDelegate().getLocalName(element);
//註冊解析器
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
@Override
public final BeanDefinition parse(Element element, ParserContext parserContext) {
AbstractBeanDefinition definition = parseInternal(element, parserContext);
if (definition != null && !parserContext.isNested()) {
try {
String id = resolveId(element, definition, parserContext);
if (!StringUtils.hasText(id)) {
parserContext.getReaderContext().error(
"Id is required for element '" + parserContext.getDelegate().getLocalName(element)
+ "' when used as a top-level tag", element);
}
String[] aliases = new String[0];
String name = element.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(name)) {
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
}
//將AbstractBeanDefinition轉化為BeanDefinitionHolder,並進行註冊
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
registerBeanDefinition(holder, parserContext.getRegistry());
if (shouldFireEvents()) {
//需要通知監聽器則進行處理
BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
postProcessComponentDefinition(componentDefinition);
parserContext.registerComponent(componentDefinition);
}
}
catch (BeanDefinitionStoreException ex) {
parserContext.getReaderContext().error(ex.getMessage(), element);
return null;
}
}
return definition;
}
從程式碼中可以看出真正進行解析的事情委託給了函式AbstractBeanDefinition definition = parseInternal(element, parserContext);
public abstract class AbstractSingleBeanDefinitionParser extends AbstractBeanDefinitionParser {
/**
* Creates a {@link BeanDefinitionBuilder} instance for the
* {@link #getBeanClass bean Class} and passes it to the
* {@link #doParse} strategy method.
* @param element the element that is to be parsed into a single BeanDefinition
* @param parserContext the object encapsulating the current state of the parsing process
* @return the BeanDefinition resulting from the parsing of the supplied {@link Element}
* @throws IllegalStateException if the bean {@link Class} returned from
* {@link #getBeanClass(org.w3c.dom.Element)} is {@code null}
* @see #doParse
*/
@Override
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
String parentName = getParentName(element);
if (parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
//獲取自定義標籤中的class
Class<?> beanClass = getBeanClass(element);
if (beanClass != null) {
builder.getRawBeanDefinition().setBeanClass(beanClass);
}
else {
String beanClassName = getBeanClassName(element);
if (beanClassName != null) {
builder.getRawBeanDefinition().setBeanClassName(beanClassName);
}
}
builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
if (parserContext.isNested()) {
// Inner bean definition must receive same scope as containing bean.
//若存在父類則使用父類的scope屬性
builder.setScope(parserContext.getContainingBeanDefinition().getScope());
}
if (parserContext.isDefaultLazyInit()) {
// Default-lazy-init applies to custom bean definitions as well.
//配置延遲載入
builder.setLazyInit(true);
}
//呼叫子類重寫的方法
doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}
/**
* Parse the supplied {@link Element} and populate the supplied
* {@link BeanDefinitionBuilder} as required.
* <p>The default implementation delegates to the {@code doParse}
* version without ParserContext argument.
* @param element the XML element being parsed
* @param parserContext the object encapsulating the current state of the parsing process
* @param builder used to define the {@code BeanDefinition}
* @see #doParse(Element, BeanDefinitionBuilder)
*/
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
doParse(element, builder);
}
在parseInternal中並不是直接呼叫子定義的doParse函式,而是進行了一些列的資料準備,包括,beanClass、scope、lazyInit等屬性的準備。
/**
* Abstract parser for <context:property-.../> elements.
*
* @author Juergen Hoeller
* @author Arjen Poutsma
* @author Dave Syer
* @since 2.5.2
*/
abstract class AbstractPropertyLoadingBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected boolean shouldGenerateId() {
return true;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String location = element.getAttribute("location");
if (StringUtils.hasLength(location)) {
String[] locations = StringUtils.commaDelimitedListToStringArray(location);
builder.addPropertyValue("locations", locations);
}
String propertiesRef = element.getAttribute("properties-ref");
if (StringUtils.hasLength(propertiesRef)) {
builder.addPropertyReference("properties", propertiesRef);
}
String fileEncoding = element.getAttribute("file-encoding");
if (StringUtils.hasLength(fileEncoding)) {
builder.addPropertyValue("fileEncoding", fileEncoding);
}
String order = element.getAttribute("order");
if (StringUtils.hasLength(order)) {
builder.addPropertyValue("order", Integer.valueOf(order));
}
builder.addPropertyValue("ignoreResourceNotFound",
Boolean.valueOf(element.getAttribute("ignore-resource-not-found")));
builder.addPropertyValue("localOverride",
Boolean.valueOf(element.getAttribute("local-override")));
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
}
}
我們已經瞭解spring將bean從配置檔案解析到記憶體的全部過程,接下來我們就要詳細介紹spring中bean的載入過程了。