前言
内容主要参考自《Spring源码深度解析》一书,算是读书笔记或是原书的补充。进入正文后可能会引来各种不适,毕竟阅读源码是件极其痛苦的事情。
本文主要涉及书中第四章的部分,依照书中内容以及个人理解对Spring进行了注释,详见Github仓库:https://github.com/MrSorrow/spring-framework
在上一篇文章中,我们已经完成了分析Spring容器对四大默认标签的解析。Spring容器通过将配置文件转换为 Document
文档对象,提取出 root 元素后,开始了所有元素的解析。解析过程中便区分了默认标签与自定义标签。
/**
* 解析注册每一个<beans>...</beans>,默认的或者自定义的如:<tx: .../>
* 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) {
// 对beans的处理
// 判断是否是自定义bean声明
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;
// 判断是否是自定义bean声明
if (delegate.isDefaultNamespace(ele)) {
// 对bean默认标签的处理
parseDefaultElement(ele, delegate);
}
else {
// 处理自定义标签 被<beans>包裹
delegate.parseCustomElement(ele);
}
}
}
}
else {
// 处理自定义标签 <beans>级别
delegate.parseCustomElement(root);
}
}
本文内容则主要探究 delegate.parseCustomElement(root)
。Spring解析配置文件标签时,先查看其命名空间然后决定默认解析还是自定义解析。分析自定义标签前,先要把握如何使用Spring的自定义标签。
I. 使用自定义标签
Spring提供我们一种可扩展的 Schema 的支持,能够扩展自定义标签,使得我们能够为自己的系统提供可配置化支持,实现起来也相对容易一些。
扩展Spring自定义标签配置步骤如下:
- 创建一个需要扩展的组件(bean实体类);
- 定义一个XSD文件描述组件内容;
- 创建一个自定义标签解析类,实现
BeanDefinitionParser
接口,用于解析XSD文件中的定义和组件定义; - 创建一个Handler类,继承自
NamespaceHandlerSupport
类,目的是将组件注册到Spring容器; - 编写 spring.handlers 和 spring.schemas 文件,用于指定本地 XSD 文件位置以及 Handler 类路径。
① 创建扩展组件
首先创建两个实体类,分别是用户和订单类。
用户 User
类:
package guo.ping.ioc.customtag;
/**
* @description: User实体类
* @author: guoping wang
* @date: 2018/9/2 13:48
* @project: spring
*/
public class User {
private String userName;
private String email;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", email='" + email + '\'' +
'}';
}
}
订单 Order
类:
package guo.ping.ioc.customtag;
/**
* @description: 订单类
* @author: guoping wang
* @date: 2018/9/3 9:12
* @project: spring
*/
public class Order {
private String orderId;
private String item;
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
@Override
public String toString() {
return "Order{" +
"orderId='" + orderId + '\'' +
", item='" + item + '\'' +
'}';
}
}
② 定义XSD文件描述组件内容
在 spring-mytest 模块(之前创建的)中 resources 文件夹下创建 META-INF 子文件夹,并在该文件夹下定义XSD文件,描述用户和订单组件内容:
<?xml version='1.0' encoding='UTF-8' ?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.wangguoping.club/schema/custom"
xmlns:tns="http://www.wangguoping.club/schema/custom"
elementFormDefault="qualified">
<element name="user">
<complexType>
<attribute name="id" type="string" />
<attribute name="userName" type="string" />
<attribute name="email" type="string" />
</complexType>
</element>
<element name="order">
<complexType>
<attribute name="id" type="string" />
<attribute name="orderId" type="string" />
<attribute name="item" type="string" />
</complexType>
</element>
</schema>
注意最开始的 schema 标签的几个属性值的设置,后面会在定义 spring.handlers 和 spring.schemas 文件进行相对应。在上面的XSD文件中描述了一个新的 targetNamespace,并在该空间定义了两个 element,分别用于描述用户和订单组件,element 内的标签与组件的属性相对应。XSD文件是XML DTD的替代者,使用XML Schema语言进行编写。
③ 创建自定义Parser
创建解析XSD文件的自定义解析类,来专门解析上面创建的xsd文件。先看用户标签解析类:
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* @description: 自定义User的BeanDefinition解析类
* @author: guoping wang
* @date: 2018/9/2 14:09
* @project: spring
*/
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
// Element 对应的类
protected Class<?> getBeanClass(Element element) {
return User.class;
}
// 从 element 中解析并提取对应的元素
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String userName = element.getAttribute("userName");
String email = element.getAttribute("email");
// 将提取的数据放入到BeanDefinitionBuilder中,待到完成所有的bean的解析后统一注册到beanFactory中
if (StringUtils.hasText(userName)) {
builder.addPropertyValue("userName", userName);
}
if (StringUtils.hasText(email)) {
builder.addPropertyValue("email", email);
}
}
}
订单标签解析类:
package guo.ping.ioc.customtag;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* @description: 自定义Order的BeanDefinition解析类
* @author: guoping wang
* @date: 2018/9/3 9:24
* @project: spring
*/
public class OrderBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return Order.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String orderId = element.getAttribute("orderId");
String item = element.getAttribute("item");
// 将提取的数据放入到BeanDefinitionBuilder中,待到完成所有的bean的解析后统一注册到beanFactory中
if (StringUtils.hasText(orderId)) {
builder.addPropertyValue("orderId", orderId);
}
if (StringUtils.hasText(item)) {
builder.addPropertyValue("item", item);
}
}
}
④ 自定义Handler
创建 Handler 类继承自 NamespaceHandlerSupport
类,目的是将组件注册到Spring容器,
package guo.ping.ioc.customtag;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
/**
* @description: 将组件注册到Spring容器
* @author: guoping wang
* @date: 2018/9/2 14:37
* @project: spring
*/
public class MyNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
registerBeanDefinitionParser("order", new OrderBeanDefinitionParser());
}
}
上面代码意思很简单,就是类似把我们自定义的解析类注册一下的操作。如果有标签携带着 user 或 order 这样的指定解析器的,就可以对应的寻找自定义的解析类来进行解析了。
⑤ 编写spring.handlers和spring.schemas
同样在 META-INF 文件夹下创建 spring.handlers 和 spring.schemas 文件。之前我们在研究 EntityResolver
用法的时候,说过Spring不一定会去网络上寻找xsd文件,如果指定了本地的xsd,则读取本地的xsd文件。同样,这里的两个文件分别指定了自定义 handler 所在路径和 xsd 文件所在路径。
spring.handlers:
http\://www.wangguoping.club/schema/custom=guo.ping.ioc.customtag.MyNamespaceHandler
spring.schemas:
http\://www.wangguoping.club/schema/custom.xsd=META-INF/spring-customtest.xsd
⑥ 测试自定义标签
经过上述五个步骤,我们便完成了自定义标签的定义与解析。我们编写一个新的Spring配置文件 customTag-Text.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:myname="http://www.wangguoping.club/schema/custom"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.wangguoping.club/schema/custom http://www.wangguoping.club/schema/custom.xsd">
<myname:user id="testUserBean" userName="wangguoping" email="[email protected]" />
<myname:order id="testOrderBean" orderId="123456" item="饼干" />
</beans>
在 spring-mytest 模块中引入 spring-context :
compile(project(":spring-context"))
新建测试类 CustomTagTest
:
package guo.ping.ioc.bean;
import guo.ping.ioc.customtag.Order;
import guo.ping.ioc.customtag.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @description:
* @author: guoping wang
* @date: 2018/9/2 14:50
* @project: spring
*/
public class CustomTagTest {
@Test
public void testCustomTag() {
ApplicationContext context = new ClassPathXmlApplicationContext("customTag-Test.xml");
User user = (User) context.getBean("testUserBean");
Order order = (Order) context.getBean("testOrderBean");
System.out.println(user);
System.out.println(order);
}
}
测试结果:
在上面的例子中,我们实现了自定义标签通过属性的方式给用户和订单类型的 Bean 进行赋值,Spring的自定义标签其实很多,例如常用的 tx :<tx: annotation-driven> 。
最后附上 spring-mytest 模块的工程文件图,便于读者复现。
II. 自定义标签解析
学会如何使用自定义标签,我们其实可以猜测Spring是如何去实现自定义标签的解析过程的。首先,读取XML配置文件的中自定义标签,通过查看自定义标签的命名空间对应到 spring.handlers 和 spring.schemas 文件去寻找 XSD 文件和 Handler 处理类,然后通过 Handler 类中注册的解析器解析标签内容,实现自定义标签的解析注册。实际上,Spring大体的流程就是如此,当然实现过程复杂的多。
我们进入 delegate.parseCustomElement(ele)
方法的源码,该方法的返回值即是Spring配置文件的表现形式 beanDefinition:
/**
* 解析处理自定义标签
* @param ele
* @return
*/
@Nullable
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
/**
* 解析处理自定义标签
* @param ele
* @param containingBd 父类bean,对顶层元素的解析应设置为null
* @return
*/
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 获取自定义标签的命名空间
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 根据命名空间查找对应的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;
}
// 调用NameSpaceHandler的进行解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
获取标签的命名空间
自定义标签的解析需要先获取标签的命名空间,org.w3c.dom.Node 类中提供了获取元素标签命名空间的方法。
/**
* 获取node的命名空间
* Get the namespace URI for the supplied node.
* <p>The default implementation uses {@link Node#getNamespaceURI}.
* Subclasses may override the default implementation to provide a
* different namespace identification mechanism.
* @param node the node
*/
@Nullable
public String getNamespaceURI(Node node) {
return node.getNamespaceURI();
}
提取自定义标签处理器
有了命名空间就能够提取 NameSpaceHandler
,提取过程是通过 this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri)
实现的。在 readerContext
初始化的时候其属性 namespaceHandlerResolver
已经被初始化为了 DefaultNamespaceHandlerResolver
类的实例。this.readerContext
是 XmlReaderContext
实例,在创建该对象的时候传入的 DefaultNamespaceHandlerResolver
的实例:
/**
* 创建XmlReaderContext对象用于registerBeanDefinitions
* Create the {@link XmlReaderContext} to pass over to the document reader.
*/
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
/**
* Lazily create a default NamespaceHandlerResolver, if not set before.
* @see #createDefaultNamespaceHandlerResolver()
*/
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}
/**
* Create the default implementation of {@link NamespaceHandlerResolver} used if none is specified.
* Default implementation returns an instance of {@link DefaultNamespaceHandlerResolver}.
*/
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
return new DefaultNamespaceHandlerResolver(cl);
}
我们查看 DefaultNamespaceHandlerResolver
中的 resolver
方法内容:
/**
* 根据node的命名空间找到指定的自定义NamespaceHandler
* 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
@Nullable
public NamespaceHandler resolve(String namespaceUri) {
// 获取所有已经配置的handler映射缓存
Map<String, Object> /**
* 根据node的命名空间找到指定的自定义NamespaceHandler
* 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
@Nullable
public NamespaceHandler resolve(String namespaceUri) {
// 获取所有已经配置的handler映射缓存
Map<String, Object> handlerMappings = getHandlerMappings();
// 从handlerMappings中取出namespaceUri对应的NamespaceHandler全类名
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缓存中
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);
}
}
} = getHandlerMappings();
// 从handlerMappings中取出namespaceUri对应的NamespaceHandler全类名
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缓存中
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);
}
}
}
handler 映射缓存是利用 HashMap 进行存储的,key 为命名空间,value 为 handler 类。我们进行debug展示 handlerMappings
中的内容如下:
如果没有注册过,通过反射技术获取 namespaceHandler
对象,此时该对象就是我们自定义的 MyNamespaceHandler
处理类。随后通过 namespaceHandler.init()
会调用自定义的初始化方法,我们的初始化方法是注册两个解析器:
@Override
public void init() {
registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
registerBeanDefinitionParser("order", new OrderBeanDefinitionParser());
}
注册完解析器后,将 namespaceHandler
对象记录在 handlerMappings
缓存中,因为 handlerMappings
的 value 类型为 Object
类型。
我们仔细查看一下获取所有已经配置的handler映射缓存的方法 getHandlerMappings()
:
/**
* 读取spring.handlers文件并将配置文件内容缓存至map中
* 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) {
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> mappingsToUse = new ConcurrentHashMap<>(mappings.size());
// 将Properties格式文件合并到Map格式的handlerMappings中
CollectionUtils.mergePropertiesIntoMap(mappings, mappingsToUse);
handlerMappings = mappingsToUse;
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return handlerMappings;
}
可以看到 getHandlerMappings()
函数读取了 META-INF 文件夹下的 spring.handlers 文件,本质上其实利用 PropertiesLoaderUtils
中的 loadAllProperties()
方法读取 .properties 文件。
解析标签
注册了自己的标签解析器后,Spring就可以把解析标签的工作交给自定义解析器去完成。我们继续跟踪代码:
// 调用NameSpaceHandler的进行解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
此时的 handler
已经是上一小节返回回来的 MyNamespaceHandler
类的实例。但是我们自定义 Handler 类中并没有重写 parse()
方法,需要进入父类 NamespaceHandlerSupport
:
/**
* Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is
* registered for that {@link Element}.
*/
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 寻找解析器并进行解析操作
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
解析过程首先需要通过 findParserForElement(element, parserContext)
寻找解析器:
/**
* 寻找解析器
* 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) {
// 获取元素名称,也就是<myname:user··· 中的user
String localName = parserContext.getDelegate().getLocalName(element);
// 根据localName来寻找对应的解析器,就是在自定义NamespaceHandler的初始化init方法中调用registerBeanDefinitionParser注册的
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
获取解析器是通过标签上的元素名称来对应的,在本文的示例中就是 <myname:user 的 user、<myname:order 的 order。根据 user 和 order 这样的 key 去 MyNamespaceHandler
类中 init()
方法中注册的解析器 Map 中寻找对应的 value,然后再调用 parse
方法。parse
方法是 BeanDefinitionParser
接口中定义的方法,在具体查看该方法内容前我们先了解自定义解析器和接口的关系:
我们查看该接口的实现类 AbstractBeanDefinitionParser
中的 parse
方法:
/**
* 自定义配置文件的解析,返回beanDefinition
* @param element the element that is to be parsed into one or more {@link BeanDefinition BeanDefinitions}
* @param parserContext the object encapsulating the current state of the parsing process;
* provides access to a {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}
* @return
*/
@Override
@Nullable
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转换为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) {
String msg = ex.getMessage();
parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element);
return null;
}
}
return definition;
}
这个函数中完成了对自定义标签的解析,并返回了 AbstractBeanDefinition
类的实例 definition
。不仅如此,其实函数这一操作主要通过单独一个方法 parseInternal()
完成,后续的操作将解析后的 AbstractBeanDefinition
转化为 BeanDefinitionHolder
并进行注册。关于注册方法 registerBeanDefinition()
方法之前在默认标签解析中已经分析。
其实正是 parseInternal()
方法调用了我们自定义解析器的解析方法 doParse(Element element, BeanDefinitionBuilder builder)
。在 AbstractBeanDefinition
中 parseInternal()
方法是一个需要抽象方法,需要子类进行实现。由类的继承结构,我们可以查看 AbstractSingleBeanDefinitionParser
中对应的重写:
/**
* 解析自定义标签内容
* 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 AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
String parentName = getParentName(element);
if (parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
// 获取自定义标签中的class,此时会调用自定义解析器如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));
BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
if (containingBd != null) {
// Inner bean definition must receive same scope as containing bean.
// 若存在父类则使用父类的scope属性
builder.setScope(containingBd.getScope());
}
if (parserContext.isDefaultLazyInit()) {
// Default-lazy-init applies to custom bean definitions as well.
// 配置延迟加载
builder.setLazyInit(true);
}
// 调用子类重写的doParse方法进行解析
doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}
我们可以看到最后调用了 doParse()
方法进行解析自定义标签内容,看来离解析完毕不远了!所以在直接调用 doParse()
之前还进行了一系列准备工作,包括对 beanClass、scope 和 lazyInit 等属性的准备。做完准备工作后,调用 doParse()
方法,在 AbstractSingleBeanDefinitionParser
中只是定义了该方法,Spring的本意也是让我们自定义解析器时重写该方法。
/**
* 自定义解析器解析方法,需要子类重写
* 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);
}
/**
* 自定义解析器解析方法,需要子类重写
* Parse the supplied {@link Element} and populate the supplied
* {@link BeanDefinitionBuilder} as required.
* <p>The default implementation does nothing.
* @param element the XML element being parsed
* @param builder used to define the {@code BeanDefinition}
*/
protected void doParse(Element element, BeanDefinitionBuilder builder) {
}
这样,最后返回包含自定义标签信息的 AbstractBeanDefinition
,自定义标签解析完毕。
III. 总结
回顾一下全部的自定义标签处理过程,虽然在实例中我们定义自定义解析器 UserBeanDefinitionParser
,但是在其中我们只是做了与自己业务逻辑相关的部分。不过我们没做但是并不代表没有,在这个处理过程中同样也是按照Spring中默认标签的处理方式进行,包括创建 BeanDefinition
以及进行相应默认属性的设置,对于这些工作 Spring都默默地帮我们实现了,只是暴露出一些接口来供用户实现个性化的业务。
现在,我们已经完成了将XML配置文件的信息转换为 BeanDefinition
的形式,将所有的 bean
从配置文件加载到内存中,接下来便是如何来 使用加载 这些 bean。