code-learning/spring/17-Spring-IoC 之解析自定义标签.md

595 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 【死磕 Spring】—— IoC 之解析自定义标签
**本文主要基于 Spring 5.0.6.RELEASE**
摘要: 原创出处 http://cmsblogs.com/?p=TODO 「小明哥」,谢谢!
作为「小明哥」的忠实读者,「老艿艿」略作修改,记录在理解过程中,参考的资料。
------
在分析自定义标签的解析之前,我们有必要了解自定义标签的使用。
# 1. 使用自定义标签
扩展 Spring 自定义标签配置一般需要以下几个步骤:
1. 创建一个需要扩展的组件。
2. 定义一个 XSD 文件,用于描述组件内容。
3. 创建一个实现 `org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser` 接口的类,用来解析 XSD 文件中的定义和组件定义。
4. 创建一个 Handler继承 `org.springframework.beans.factory.xml.NamespaceHandlerSupport` 抽象类 ,用于将组件注册到 Spring 容器。
5. 编写 `spring.handlers``Spring.schemas` 文件。
下面就按照上面的步骤来实现一个自定义标签组件。
## 1.1 创建组件
该组件就是一个普通的 Java Bean没有任何特别之处。代码如下
```
public class User {
private String id;
private String userName;
private String email;
}
```
## 1.2 定义 XSD 文件
```
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.cmsblogs.com/schema/user" targetNamespace="http://www.cmsblogs.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>
```
上面除了对 User 这个 Java Bean 进行了描述外,还定义了 `xmlns="http://www.cmsblogs.com/schema/user"``targetNamespace="http://www.cmsblogs.com/schema/user"` 这两个值,这两个值在后面是有大作用的。
## 1.3 定义 Parser 类
定义一个 Parser 类,该类继承 AbstractSingleBeanDefinitionParser ,并实现 `#getBeanClass(Element element)``#doParse(Element element, BeanDefinitionBuilder builder)` 两个方法。主要是用于解析 XSD 文件中的定义和组件定义。
```
public class UserDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return User.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String id = element.getAttribute("id");
String userName = element.getAttribute("userName");
String email = element.getAttribute("email");
if (StringUtils.hasText(id)) {
builder.addPropertyValue("id", id);
}
if (StringUtils.hasText(userName)) {
builder.addPropertyValue("userName", userName);
}
if (StringUtils.hasText(email)) {
builder.addPropertyValue("email", email);
}
}
}
```
## 1.4 定义 NamespaceHandler 类
定义 NamespaceHandler 类,继承 NamespaceHandlerSupport ,主要目的是将组件注册到 Spring 容器中。
```
public class UserNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("user", new UserDefinitionParser());
}
}
```
## 1.5 定义 spring.handlers 文件
```
http\://www.cmsblogs.com/schema/user=org.springframework.core.customelement.UserNamespaceHandler
```
## 1.6 定义 Spring.schemas 文件
```
http\://www.cmsblogs.com/schema/user.xsd=user.xsd
```
## 1.7 运行
经过上面几个步骤,就可以使用自定义的标签了。在 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:myTag="http://www.cmsblogs.com/schema/user"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.cmsblogs.com/schema/user http://www.cmsblogs.com/schema/user.xsd">
<myTag:user id="user" email="12233445566@qq.com" userName="chenssy" />
</beans>
```
运行测试:
```
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
User user = (User) context.getBean("user");
System.out.println(user.getUserName() + "----" + user.getEmail());
}
```
运行结果如下图:
[![运行结果](17-Spring-IoC 之解析自定义标签.assets/64a6aa7283cd9e6ae5070162ae007872.png)](http://static.iocoder.cn/64a6aa7283cd9e6ae5070162ae007872)运行结果
# 2. 解析自定义标签
上面已经演示了 Spring 自定义标签的使用,下面就来分析自定义标签的解析过程。
## 2.1 parseCustomElement
DefaultBeanDefinitionDocumentReader 的`#parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)` 方法,负责标签的解析工作,根据命名空间的不同进行不同标签的解析。其中,**自定义标签**由 BeanDefinitionParserDelegate 的 `#parseCustomElement(Element ele, BeanDefinition containingBd)` 方法来实现。代码如下:
```
@Nullable
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// <1> 获取 namespaceUri
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// <2> 根据 namespaceUri 获取相应的 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;
}
// 调用自定义的 Handler 处理
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
```
处理过程分为三步:
1. 调用 `#getNamespaceURI((Node node)` 方法,获取 `namespaceUri` 。代码如下:
```
@Nullable
public String getNamespaceURI(Node node) {
return node.getNamespaceURI();
}
```
2. 调用 `XmlReaderContext#getNamespaceHandlerResolver()` 方法,获得命名空间的解析器。详细解析,见 [「2.2 getNamespaceHandlerResolver」](https://svip.iocoder.cn/Spring/IoC-parse-BeanDefinitions-in-parseCustomElement/#) 。
3. 调用 `NamespaceHandlerResolver#resolve(String namespaceUri)` 方法,根据 `namespaceUri` 获取相应的 Handler 对象。这个映射关系我们在 `spring.handlers` 中已经定义了,所以只需要找到该类,然后初始化返回。详细解析,见 [「2.3 resolve」](https://svip.iocoder.cn/Spring/IoC-parse-BeanDefinitions-in-parseCustomElement/#) 。
4. 调用 `NamespaceHandler#parse(Element element, ParserContext parserContext)` 方法,调用自定义的 Handler 处理。详细解析,见 [「2.4 parse」](https://svip.iocoder.cn/Spring/IoC-parse-BeanDefinitions-in-parseCustomElement/#) 。
## 2.2 getNamespaceHandlerResolver
调用 XmlReaderContext 的 `#getNamespaceHandlerResolver()` 方法,返回的命名空间的解析器,代码如下:
```
/**
* NamespaceHandler 解析器
*/
private final NamespaceHandlerResolver namespaceHandlerResolver;
public final NamespaceHandlerResolver getNamespaceHandlerResolver() {
return this.namespaceHandlerResolver;
}
```
### 2.2.1 NamespaceHandlerResolver 的初始化
那么NamespaceHandlerResolver 是什么时候进行初始化的呢?
这里需要回退到博文 [《【死磕 Spring】—— IoC 之注册 BeanDefinitions》](http://svip.iocoder.cn/Spring/IoC-register-BeanDefinitions) ,在这篇博客中提到在注册 BeanDefinition 时:
- 首先,是通过 XmlBeanDefinitionReader 的 `#createBeanDefinitionDocumentReader()` 方法,获取 Document 解析器 BeanDefinitionDocumentReader 实例。
- 然后,调用 BeanDefinitionDocumentReader 实例的 `#registerBeanDefinitions(Document doc, XmlReaderContext readerContext)` 方法,进行注册。而该方法需要提供两个参数,一个是 Document 实例 `doc`,一个是 XmlReaderContext 实例 `readerContext` 。
`readerContext` 实例对象由 XmlBeanDefinitionReader 的 `#createReaderContext(Resource resource)` 方法创建。`namespaceHandlerResolver` 实例对象就是在这个时候初始化的,代码如下:
```
// XmlBeanDefinitionReader.java
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
```
- XmlReaderContext 构造函数中最后一个参数就是 NamespaceHandlerResolver 对象,该对象由 `getNamespaceHandlerResolver()` 提供,如下:
```
// XmlBeanDefinitionReader.java
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
return new DefaultNamespaceHandlerResolver(cl); // <x>
}
```
- 从 `<x>` 处我们可以看到NamespaceHandlerResolver 对象的**最终类型**是 `org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver` 。
## 2.3 resolve
所以, `getNamespaceHandlerResolver().resolve(namespaceUri)` 调用的就是 DefaultNamespaceHandlerResolver 的 `#resolve(String namespaceUri)` 方法。代码如下:
```
@Override
@Nullable
public NamespaceHandler resolve(String namespaceUri) {
// <1> 获取所有已经配置的 Handler 映射
Map<String, Object> handlerMappings = getHandlerMappings();
// <2> 根据 namespaceUri 获取 handler 的信息
Object handlerOrClassName = handlerMappings.get(namespaceUri);
// <3.1> 不存在
if (handlerOrClassName == null) {
return null;
// <3.2> 已经初始化
} else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
// <3.3> 需要进行初始化
} else {
String className = (String) handlerOrClassName;
try {
// 获得类,并创建 NamespaceHandler 对象
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("Could not find NamespaceHandler class [" + className +
"] for namespace [" + namespaceUri + "]", ex);
} catch (LinkageError err) {
throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
className + "] for namespace [" + namespaceUri + "]", err);
}
}
}
```
- `<1>` 处,首先,调用 `#getHandlerMappings()` 方法,获取所有配置文件中的映射关系 `handlerMappings` 。详细解析,胖友先跳到 [「2.3.1 getHandlerMappings」](https://svip.iocoder.cn/Spring/IoC-parse-BeanDefinitions-in-parseCustomElement/#) ,看完就回到此处,继续往下走。
- `<2>` 处,然后,根据 `namespaceUri` 获取 handler 的信息。
- `<3.1>` 处,`handlerOrClassName` 不存在,则返回 `null` 空。
- `<3.2>` 处,`handlerOrClassName` 已经初始化成 NamespaceHandler 对象,直接返回它。
- ```
<3.3>
```
处,
```
handlerOrClassName
```
还是类路径,则创建 NamespaceHandler 对象,并调用
```
NamespaceHandler#init()
```
方法,初始化 NamespaceHandler 对象。详细解析,见
「2.3.2 init」
- 另外,创建的 NamespaceHandler 对象,会添加到 `handlerMappings` 中,进行缓存。
### 2.3.1 getHandlerMappings
```
/** ClassLoader to use for NamespaceHandler classes. */
@Nullable
private final ClassLoader classLoader;
/**
* NamespaceHandler 映射配置文件地址
*
* Resource location to search for.
*/
private final String handlerMappingsLocation;
/**
* Stores the mappings from namespace URI to NamespaceHandler class name / instance.
*
* NamespaceHandler 映射。
*
* key命名空间
* value分成两种情况1未初始化时对应的 NamespaceHandler 的类路径2已初始化对应的 NamespaceHandler 对象
*/
@Nullable
private volatile Map<String, Object> handlerMappings;
/**
* Load the specified NamespaceHandler mappings lazily.
*/
private Map<String, Object> getHandlerMappings() {
// 双重检查锁,延迟加载
Map<String, Object> handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
synchronized (this) {
handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
if (logger.isTraceEnabled()) {
logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
}
try {
// 读取 handlerMappingsLocation
Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isTraceEnabled()) {
logger.trace("Loaded NamespaceHandler mappings: " + mappings);
}
// 初始化到 handlerMappings 中
handlerMappings = new ConcurrentHashMap<>(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 handlerMappings;
}
```
- 虽然代码比较长,但是逻辑实际很简单。
- 通过延迟加载( lazy-init )的方式,加载 `handlerMappingsLocation` 中配置的 NamespaceHandler 的映射,到 `handlerMappings` 中。
- `handlerMappings` 的**值属性**有 2 种情况,胖友仔细看下注释。
### 2.3.2 init
实现 NamespaceHandler 的 `#init()` 方法,主要是将自定义标签解析器进行注册。例如,我们自定义 UserNamespaceHandler 的 `#init()` 方法,代码如下:
```
// UserNamespaceHandler.java
@Override
public void init() {
registerBeanDefinitionParser("user",new UserDefinitionParser());
}
```
- 直接调用父类 NamespaceHandlerSupport 的 `#registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser)` 方法,注册指定元素的 BeanDefinitionParser 解析器。
#### 2.3.2.1 registerBeanDefinitionParser
NamespaceHandlerSupport 的 `#registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser)` 方法,注册指定元素的 BeanDefinitionParser 解析器。代码如下:
```
// NamespaceHandlerSupport.java
/**
* Stores the {@link BeanDefinitionParser} implementations keyed by the
* local name of the {@link Element Elements} they handle.
*
* key元素名
* value对应 BeanDefinitionParser 的解析器
*/
private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}
```
- 其实就是将映射关系放在一个 Map 结构的 `parsers` 对象中。
## 2.4 parse
完成后返回 NamespaceHandler 对象,然后调用其 `#parse(Element element, ParserContext parserContext)` 方法开始自定义标签的解析。代码如下:
```
// NamespaceHandlerSupport.java
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
// <1> 获得元素对应的 BeanDefinitionParser 对象
BeanDefinitionParser parser = findParserForElement(element, parserContext);
// <2> 执行解析
return (parser != null ? parser.parse(element, parserContext) : null);
}
```
- `<1>` 处,调用 `#findParserForElement(Element element, ParserContext parserContext)` 方法,获取对应的 BeanDefinitionParser 实例。实际上,其实就是获取在 NamespaceHandlerSupport 的 `#registerBeanDefinitionParser()` 方法里面注册的实例对象。代码如下:
```
/**
* Locates the {@link BeanDefinitionParser} from the register implementations using
* the local name of the supplied {@link Element}.
*/
@Nullable
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
// 获得元素名
String localName = parserContext.getDelegate().getLocalName(element);
// 获得 BeanDefinitionParser 对象
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
```
- 首先,获取 `localName`,在上面的例子中就是:`"user` 。
- 然后,从 Map 实例 `parsers` 中获取 BeanDefinitionParser 对象。在上面的例子中就是UserBeanDefinitionParser 对象。
- `<2>` 处,返回 BeanDefinitionParser 对象后,调用其 `#parse(Element element, ParserContext parserContext)` 方法。该方法在 AbstractBeanDefinitionParser 中实现,代码如下:
```
// AbstractBeanDefinitionParser.java
@Override
@Nullable
public final BeanDefinition parse(Element element, ParserContext parserContext) {
// <1> 内部解析,返回 AbstractBeanDefinition 对象
AbstractBeanDefinition definition = parseInternal(element, parserContext);
if (definition != null && !parserContext.isNested()) {
try {
// 解析 id 属性
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);
}
// 解析 aliases 属性
String[] aliases = null;
if (shouldParseNameAsAliases()) {
String name = element.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(name)) {
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
}
}
// 创建 BeanDefinitionHolder 对象
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
// 注册 BeanDefinition
registerBeanDefinition(holder, parserContext.getRegistry());
// 触发事件
if (shouldFireEvents()) {
BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
postProcessComponentDefinition(componentDefinition);
parserContext.registerComponent(componentDefinition);
}
} catch (BeanDefinitionStoreException ex) {
String msg = ex.getMessage();
parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element);
return null;
}
}
return definition;
}
```
- 核心在 `<1>` 处 `#parseInternal(Element element, ParserContext parserContext)` 方法。为什么这么说?因为该方法返回的是 AbstractBeanDefinition 对象。从前面**默认标签**的解析过程来看,我们就可以判断该方法就是将标签解析为 AbstractBeanDefinition ,且后续代码都是将 AbstractBeanDefinition 转换为 BeanDefinitionHolder 对象。所以真正的解析工作都交由 `#parseInternal(Element element, ParserContext parserContext)` 方法来实现。关于该方法,详细解析,见 [「2.4.1 parseInternal」](https://svip.iocoder.cn/Spring/IoC-parse-BeanDefinitions-in-parseCustomElement/#) 。
- 其它逻辑,例如 `#resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)` 方法,都比较简单,感兴趣的胖友,可以自己去看。
### 2.4.1 parseInternal
`#parseInternal(Element element, ParserContext parserContext)` 方法,解析 XML 元素为 AbstractBeanDefinition 对象。代码如下:
```
// AbstractSingleBeanDefinitionParser.java
@Override
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
// 创建 BeanDefinitionBuilder 对象
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
// 获取父类元素
String parentName = getParentName(element);
if (parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
// 获取自定义标签中的 class这个时候会去调用自定义解析中的 getBeanClass()
Class<?> beanClass = getBeanClass(element);
if (beanClass != null) {
builder.getRawBeanDefinition().setBeanClass(beanClass);
} else {
// beanClass 为 null意味着子类并没有重写 getBeanClass() 方法,则尝试去判断是否重写了 getBeanClassName()
String beanClassName = getBeanClassName(element);
if (beanClassName != null) {
builder.getRawBeanDefinition().setBeanClassName(beanClassName);
}
}
// 设置 source 属性
builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
// 设置 scope 属性
BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
if (containingBd != null) {
// Inner bean definition must receive same scope as containing bean.
builder.setScope(containingBd.getScope());
}
// 设置 lazy-init 属性
if (parserContext.isDefaultLazyInit()) {
// Default-lazy-init applies to custom bean definitions as well.
builder.setLazyInit(true);
}
// 调用子类的 doParse() 进行解析
doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}
```
- 在该方法中我们主要关注两个方法:`#getBeanClass((Element element)` 、`#doParse(Element element, BeanDefinitionBuilder builder)`。
- 对于 `getBeanClass()` 方法AbstractSingleBeanDefinitionParser 类并没有提供具体实现,而是直接返回 `null` **意味着它希望子类能够重写该方法**。当然,如果没有重写该方法,这会去调用 `#getBeanClassName()` ,判断子类是否已经重写了该方法。
- 对于 `#doParse(Element element, BeanDefinitionBuilder builder)` 方法,则是直接**空实现**。
😈 所以对于 `#parseInternal(Element element, ParserContext parserContext)` 方法 而言,它总是期待它的子类能够实现 `#getBeanClass((Element element)` 、`#doParse(Element element, BeanDefinitionBuilder builder)` 方法。其中,`#doParse(Element element, BeanDefinitionBuilder builder)` **方法尤为重要**!如果,你不提供该方法的实现,怎么来解析自定义标签呢?此时,胖友可以回过头,再看一眼在 [「1.3 定义 Parser 类」](https://svip.iocoder.cn/Spring/IoC-parse-BeanDefinitions-in-parseCustomElement/#) 的 UserDefinitionParser 实现类,是不是已经能够很好理解咧。
# 3. 小结
至此,自定义标签的解析过程已经分析完成了。其实整个过程还是较为简单:
- 首先,会加载 `spring.handlers` 文件,将其中内容进行一个解析,形成 `<namespaceUri, 类路径>` 这样的一个映射。
- 然后,根据获取的 `namespaceUri` 就可以得到相应的类路径,对其进行初始化等到相应的 NamespaceHandler 对象。
- 之后,调用该 NamespaceHandler 的 `#parse(...)` 方法,在该方法中根据标签的 `localName` 得到相应的 BeanDefinitionParser 实例对象。
- 最后,调用该 BeanDefinitionParser 的 `#parse(...)` 方法。该方法定义在 AbstractBeanDefinitionParser 抽象类中,核心逻辑封装在其 `#parseInternal(...)` 方法中,该方法返回一个 AbstractBeanDefinition 实例对象,其主要是在 AbstractSingleBeanDefinitionParser 中实现。对于自定义的 Parser 类,其需要实现 `#getBeanClass()` 或者 `#getBeanClassName()` 任一方法,和 `#doParse(...)` 方法。
整体流程如[下图](https://gitee.com/chenssy/blog-home/raw/master/image/201811/spring-201807151001.png)
[![spring-201807151001](17-Spring-IoC 之解析自定义标签.assets/b53b2484800ee0e35be4510d9ebe2891.png)](http://static.iocoder.cn/b53b2484800ee0e35be4510d9ebe2891)spring-201807151001