参考文章:
自定义的标签解析
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 获取 namespaceUri
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 根据 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));
}
使用自定义标签
扩展 Spring 自定义标签配置一般需要以下几个步骤:
- 创建一个需要扩展的组件。
- 定义一个 XSD 文件,用于描述组件内容。
- 创建一个实现
org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
接口的类,用来解析 XSD 文件中的定义和组件定义。- 创建一个 Handler,继承
org.springframework.beans.factory.xml.NamespaceHandlerSupport
抽象类 ,用于将组件注册到 Spring 容器。- 编写
spring.handlers
和Spring.schemas
文件。
创建组件
普通的 Java Bean
public class User {
private String id;
private String userName;
private String email;
}
定义 XSD 文件
对 User 这个 Java Bean 进行了描述外,还定义了
xmlns="http://www.cmsblogs.com/schema/user"
和targetNamespace="http://www.cmsblogs.com/schema/user"
这两个值
<?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>
定义 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);
}
}
}
定义 NamespaceHandler 类
定义 NamespaceHandler 类,继承 NamespaceHandlerSupport ,主要目的是将组件注册到 Spring 容器中。
public class UserNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("user", new UserDefinitionParser());
}
}
定义 spring.handlers 文件
http\://www.cmsblogs.com/schema/user=org.springframework.core.customelement.UserNamespaceHandler
定义 Spring.schemas 文件
http\://www.cmsblogs.com/schema/user.xsd=user.xsd
运行
在 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());
}
解析自定义标签
parseCustomElement
负责标签的解析工作,根据命名空间的不同进行不同标签的解析。其中,自定义标签由 BeanDefinitionParserDelegate 的
#parseCustomElement(Element ele, BeanDefinition containingBd)
方法来实现
下面的代码在标签解析中以及分析过一次
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 获取 namespaceUri
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 根据 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));
}
getNamespaceHandlerResolver
返回的命名空间的解析器
public final NamespaceHandlerResolver getNamespaceHandlerResolver() {
return this.namespaceHandlerResolver;
}
NamespaceHandlerResolver 的初始化
readerContext
实例对象由 XmlBeanDefinitionReader 的#createReaderContext(Resource resource)
方法创建。namespaceHandlerResolver
实例对象就是在这个时候初始化的
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
//假如reader还不存在namespaceHandlerResolver,则使用默认值
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}
resolve
@Override
@Nullable
public NamespaceHandler resolve(String namespaceUri) {
//获取所有已经配置的 Handler 映射
Map<String, Object> handlerMappings = getHandlerMappings();
//根据 namespaceUri 获取 handler 的信息
Object handlerOrClassName = handlerMappings.get(namespaceUri);
//不存在
if (handlerOrClassName == null) {
return null;
}
//已经初始化
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
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);
}
}
}
getHandlerMappings
- 获取所有配置文件中的映射关系
private Map<String, Object> getHandlerMappings() {
// key:命名空间
// value:分成两种情况:1)未初始化时,对应的 NamespaceHandler 的类路径;2)已初始化,对应的 NamespaceHandler 对象
Map<String, Object> handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
// 通过延迟加载( lazy-init )的方式,
// 加载 handlerMappingsLocation 中配置的 NamespaceHandler 的映射,到 handlerMappings
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;
}
parse
完成后返回 NamespaceHandler 对象,然后调用其
#parse(Element element, ParserContext parserContext)
方法开始自定义标签的解析
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
public BeanDefinition parse(Element element, ParserContext parserContext) {
//获得元素对应的 BeanDefinitionParser 对象
BeanDefinitionParser parser = findParserForElement(element, parserContext);
//执行解析
return (parser != null ? parser.parse(element, parserContext) : null);
}
调用
#findParserForElement(Element element, ParserContext parserContext)
方法,获取对应的 BeanDefinitionParser 实例。实际上,其实就是获取在 NamespaceHandlerSupport 的#registerBeanDefinitionParser()
方法里面注册的实例对象
@Nullable
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
// 获得元素名
String localName = parserContext.getDelegate().getLocalName(element);
// 从 Map 实例 parsers 中获取 BeanDefinitionParser 对象
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
返回 BeanDefinitionParser 对象后,调用其
#parse(Element element, ParserContext parserContext)
方法。该方法在 AbstractBeanDefinitionParser 中实现
public final BeanDefinition parse(Element element, ParserContext parserContext) {
//内部解析,返回 AbstractBeanDefinition 对象 真正的解析工作都交由 #parseInternal
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);
}
//获得别名
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;
}
整个类的解析过程
- 会加载
spring.handlers
文件,将其中内容进行一个解析,形成<namespaceUri, 类路径>
这样的一个映射- 根据获取的
namespaceUri
就可以得到相应的类路径,对其进行初始化等到相应的 NamespaceHandler 对象- 调用该 NamespaceHandler 的
#parse(...)
方法,在该方法中根据标签的localName
得到相应的 BeanDefinitionParser 实例对象- 调用该 BeanDefinitionParser 的
#parse(...)
方法。该方法定义在 AbstractBeanDefinitionParser 抽象类中,核心逻辑封装在其#parseInternal(...)
方法中,该方法返回一个 AbstractBeanDefinition 实例对象,其主要是在 AbstractSingleBeanDefinitionParser 中实现。其需要实现#getBeanClass()
或者#getBeanClassName()
任一方法