Spring源碼分析-bean的解析(3)
當(dāng)前版本 Spring 4.3.8
自定義標(biāo)簽的解析
自定義標(biāo)簽使用
在很多情況下,當(dāng)使用默認(rèn)配置過于繁瑣時(shí)候,解析工作或許是一個(gè)不得不考慮的負(fù)擔(dān)。Spring 提供了可擴(kuò)展 Scheme 的支持。大概需要以下幾個(gè)步驟:
-
創(chuàng)建一個(gè)需要擴(kuò)展的組件
package io.github.binglau.bean; import lombok.Data; /** * 文件描述: */ @Data public class User { private String userName; private String email; }
-
定義一個(gè) XSD 文件描述組件內(nèi)容
<?xml version="1.0" encoding="UTF-8" ?> <xsd:schema xmlns="http://www.binglau.com/schema/user" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.binglau.com/schema/user" elementFormDefault="qualified"> <xsd:element name="user"> <xsd:complexType> <xsd:attribute name="id" type="xsd:string"/> <xsd:attribute name="userName" type="xsd:string"/> <xsd:attribute name="email" type="xsd:string"/> </xsd:complexType> </xsd:element> </xsd:schema>
-
創(chuàng)建一個(gè)文件,實(shí)現(xiàn)
BeanDefinitionParser
接口,用來解析 XSD 文件中的定義和組件定義package io.github.binglau; import io.github.binglau.bean.User; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.util.StringUtils; import org.w3c.dom.Element; /** * 文件描述: */ public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { // Element 對(duì)應(yīng)的類 @Override protected Class<?> getBeanClass(Element element) { return User.class; } // 從 element 中解析并提取對(duì)應(yīng)的元素 @Override protected void doParse(Element element, BeanDefinitionBuilder builder) { String userName = element.getAttribute("userName"); String email = element.getAttribute("email"); // 將提取的數(shù)據(jù)放入 BeanDefinitionBuilder 中,待到完成所有 bean 的解析后統(tǒng)一注冊(cè)到 beanFactory 中 if (StringUtils.hasText(userName)) { builder.addPropertyValue("userName", userName); } if (StringUtils.hasText(email)) { builder.addPropertyValue("email", email); } } }
-
創(chuàng)建一個(gè) Handler 文件,擴(kuò)展自 NamespaceHandlerSupport,目的是將組件注冊(cè)到 Spring 容器
package io.github.binglau; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; /** * 文件描述: */ public class MyNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("user", new UserBeanDefinitionParser()); } }
-
編寫 Spring.handlers 和 Spring.schemas 文件(resources/MATE-INF)
# Spring.handlers http\://www.binglau.com/schema/user=io.github.binglau.MyNamespaceHandler # Spring.schemas http\://www.binglau.com/schema/user.xsd=user-xsd.xsd
測(cè)試:
<?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:myname="http://www.binglau.com/schema/user"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.binglau.com/schema/user http://www.binglau.com/schema/user.xsd">
<myname:user id="testbean" userName="aaa" email="bbb"/>
<bean id="testBean" class="io.github.binglau.bean.TestBean" />
</beans>
package io.github.binglau;
import io.github.binglau.bean.User;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
/**
* 文件描述:
*/
public class BeanFactoryTest {
@Test
public void testSimpleLoad() {
BeanFactory context = new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));
User user = (User)context.getBean("testbean");
System.out.println(user);
}
}
/**
結(jié)果:
User(userName=aaa, email=bbb)
**/
自定義標(biāo)簽解析
/**
* 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(ele)
既是對(duì)自定義標(biāo)簽的解析
BeanDefinitionParserDelegate.parseCustomElement
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
// containingBd 為父類 bean,對(duì)頂層元素的解析應(yīng)設(shè)置為 null
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
// 獲取對(duì)應(yīng)的命名空間
String namespaceUri = getNamespaceURI(ele);
// 根據(jù)命名空間找到對(duì)應(yīng)的 NamespaceHandler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
// 調(diào)用自定義的 NamespaceHandler 進(jìn)行解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
獲取標(biāo)簽的命名空間
直接調(diào)用 org.w3c.dom.Node
中的方法
public String getNamespaceURI(Node node) {
return node.getNamespaceURI();
}
提取自定義標(biāo)簽處理器
private final XmlReaderContext readerContext
即 new ClassPathResource("beanFactory.xml")
在 readerContext
初始化的時(shí)候其屬性 namespaceHandlerResolver
已經(jīng)被初始化為 DefaultNamespaceHandlerResolver
實(shí)例,所以,這里實(shí)際調(diào)用了 DefaultNamespaceHandlerResolver
的方法。
/**
* Locate the {@link NamespaceHandler} for the supplied namespace URI
* from the configured mappings.
* @param namespaceUri the relevant namespace URI
* @return the located {@link NamespaceHandler}, or {@code null} if none found
*/
@Override
public NamespaceHandler resolve(String namespaceUri) {
// 獲取所有已有配置的 handler 映射
Map<String, Object> handlerMappings = getHandlerMappings();
// 根據(jù)命名空間找到對(duì)應(yīng)的信息
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
else if (handlerOrClassName instanceof NamespaceHandler) {
// 已經(jīng)做過解析的情況,直接從緩存讀取
return (NamespaceHandler) handlerOrClassName;
}
else {
// 沒有做過解析,則返回的是類路徑
String className = (String) handlerOrClassName;
try {
// 使用反射 將類路徑轉(zhuǎn)換為類
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);
// 調(diào)用自定義的 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);
}
}
}
在上面的調(diào)用namespaceHandler.init();
中參考之前的自定義標(biāo)簽使用
public void init() {
registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
}
當(dāng)?shù)玫阶远x命名空間處理后回馬上進(jìn)行 BeanDefinitionParser
的注冊(cè)以支持自定義標(biāo)簽
注冊(cè)后,命名空間處理器就可以根據(jù)標(biāo)簽的不同來調(diào)用不同的解析器進(jìn)行解析。getHandlerMappings
主要功能就是讀取 Spring.handlers
配置文件并將配置文件緩存在 map 中。
/**
* Load the specified NamespaceHandler mappings lazily.
*/
private Map<String, Object> getHandlerMappings() {
// 如果沒有被緩存則開始進(jìn)行緩存
if (this.handlerMappings == null) {
synchronized (this) {
if (this.handlerMappings == null) {
try {
// this.handlerMappingsLocation 在構(gòu)造函數(shù)中被初始化為 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;
}
標(biāo)簽解析
NamespaceHandlerSupport#parse
/**
* 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) {
// 尋找解析器并進(jìn)行解析操作
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) {
// 獲取元素名稱,也就是 <myname:user> 中的 user,若在上面的示例中,則此時(shí) localName 為 user
String localName = parserContext.getDelegate().getLocalName(element);
// 根據(jù) user 找到對(duì)應(yīng)的解析器,也就是在
// registerBeanDefinitionParser("user", new UserBeanDefinitionParser()) 注冊(cè)的解析器
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
而對(duì)于 parse 方法的處理
AbstractBeanDefinitionParser#parse
@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 = null;
if (shouldParseNameAsAliases()) {
String name = element.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(name)) {
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
}
}
// 將 AbstractBeanDefinition 轉(zhuǎn)換為 BeanDefinitionHolder 并注冊(cè)
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
registerBeanDefinition(holder, parserContext.getRegistry());
if (shouldFireEvents()) {
// 需要通知監(jiān)聽器則進(jìn)行處理
BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
postProcessComponentDefinition(componentDefinition);
parserContext.registerComponent(componentDefinition);
}
}
catch (BeanDefinitionStoreException ex) {
parserContext.getReaderContext().error(ex.getMessage(), element);
return null;
}
}
return definition;
}
AbstractSingleBeanDefinitionParser#parseInternal
/**
* 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);
}
// 獲取自定義標(biāo)簽中的 class,此時(shí)會(huì)調(diào)用自定義解析器如 UserBeanDefinitionParser 中的 getBeanClass 方法
Class<?> beanClass = getBeanClass(element);
if (beanClass != null) {
builder.getRawBeanDefinition().setBeanClass(beanClass);
}
else {
// 若子類沒有重寫 getBeanClass 方法則嘗試檢查子類是否重寫 getBeanClassName 方法
String beanClassName = getBeanClassName(element);
if (beanClassName != null) {
builder.getRawBeanDefinition().setBeanClassName(beanClassName);
}
}
builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
if (parserContext.isNested()) {
// 若存在父類則使用父類的 scope 屬性
// Inner bean definition must receive same scope as containing bean.
builder.setScope(parserContext.getContainingBeanDefinition().getScope());
}
if (parserContext.isDefaultLazyInit()) {
// Default-lazy-init applies to custom bean definitions as well.
// 配置延遲加載
builder.setLazyInit(true);
}
// 調(diào)用子類重寫的 doParse 方法進(jìn)行解析
doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
doParse(element, builder);
}
參考書籍
《Spring源碼深度解析》