Spring版本
5.2.5.RELEASE
参考
1. demo
在《Spring源码解析-BeanDefinition注册》中,遗留了自定义标签的解析,那么本文接着补充该部分的内容。
在解析源码之前,我们先写一个demo,实现自定义标签的功能,来熟悉并帮助理解源码
1.1 项目结构
项目结构1.2 user.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="name" type="xsd:string" />
<xsd:attribute name="email" type="xsd:string" />
</xsd:complexType>
</xsd:element>
</xsd:schema>
我们自定义标签的时候,需要有标签名,标签内还有对应的各种属性,该文件就是用来定义这些数据的,比如说:
<xsd:element name="user">
指定了自定义标签的标签名称,而:
<xsd:attribute name="id" type="xsd:string" />
则定义了标签内的属性名称及其数据类型
1.3 User
package com.kungyu.custom.element;
/**
* @author wengyongcheng
* @since 2020/4/15 10:07 下午
*/
public class User {
private String id;
private String name;
private String email;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
相当于一个bean
1.4 UserDefinitionParser
package com.kungyu.custom.element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* @author wengyongcheng
* @since 2020/4/15 10:12 下午
*/
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 name = element.getAttribute("name");
String email = element.getAttribute("email");
if (StringUtils.hasText(id)) {
builder.addPropertyValue("id", id);
}
if (StringUtils.hasText(name)) {
builder.addPropertyValue("name", name);
}
if (StringUtils.hasText(email)) {
builder.addPropertyValue("email", email);
}
}
}
在xml文件中使用自定义标签,需要一个解析类来解析自定义的标签
1.5 UserNamespaceHandler
package com.kungyu.custom.element;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
/**
* @author wengyongcheng
* @since 2020/4/15 10:21 下午
*/
public class UserNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("user", new UserDefinitionParser());
}
}
注册自定义的解析器
1.6 spring.handlers
http\://www.cmsblogs.com/schema/user=com.kungyu.custom.element.UserNamespaceHandler
spring解析自定义标签的时候,默认会去读取META-INF/spring.handlers文件来获取UserNamespaceHandler
1.7 spring.schemas
http\://www.cmsblogs.com/schema/user.xsd=META-INF/user.xsd
指定自定义标签的xsd文件
1.8 spring.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" name="chenssy"/>
</beans>
1.9 测试
package com.kungyu.custom.element;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author wengyongcheng
* @since 2020/4/15 10:27 下午
*/
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
User user = (User) context.getBean("user");
System.out.println(user.getName() + "----" + user.getEmail());
}
}
结果:
运行结果
2. 源码解读
2.1 BeanDefinitionParserDelegate#parseCustomElement
@Nullable
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
调用重载方法:
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 获取命名空间uri,也就是xml文档开头处定义的ns,如
// xmlns="http://www.springframework.org/schema/beans"
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 通过namespaceUri获取到可以解析该元素的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;
}
// 执行解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
在上文的demo中,执行debug,可以看到获得到的namespaceUri为:
获取namespaceUri
该值是我们在user.xsd定义的xmlns属性的值
2.1.1 resolve
@Override
@Nullable
public NamespaceHandler resolve(String namespaceUri) {
// 获取已配置好的handleMapping
// handleMapping存在俩种情况:
// 1、NamespaceHandler对象
// 2、未初始化的NamespaceHandler类路径
Map<String, Object> handlerMappings = getHandlerMappings();
Object handlerOrClassName = handlerMappings.get(namespaceUri);
// 如果为空,直接返回,handlerMappings中存放的都是已经配置好的,意味着要么已经初始化了,要么也至少有个类路径来提供初始化
if (handlerOrClassName == null) {
return null;
}
// 对应第一种情况,已经存在的NamespaceHandler对象,直接返回
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
// 对应第二种情况,存放的是类路径
String className = (String) handlerOrClassName;
try {
// 通过类路径进行类加载
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
// 判断handlerClass是否继承自NamespaceHandler
// isAssignableFrom用于俩个class对象的判断,而instance of是实例与class的判断
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.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);
}
}
}
可以看到,该方法的作用在于根据namespaceUri解析出对应的自定义NamespaceHandler,在demo中,NamespaceHandler为UserNamespaceHandler,具体一步步解析如下:
2.1.1.1 getHandlerMappings
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默认路径:META-INF/spring.handlers
// 所以,自定义标签的时候,需要写一份spring.handlers文件
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isTraceEnabled()) {
logger.trace("Loaded NamespaceHandler mappings: " + mappings);
}
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;
}
使用了双重检查机制,获取handlerMappings缓存对象,该缓存对象为空的情况下,读取handlerMappingsLocation获取默认的handlers文件路径:
META-INF/spring.handlers
所以我们在前文的demo才需要指定一份spring.handlers文件,在getHandlerMappings方法中,将spring.handlers文件的内容解析到handlerMappings中,handlerMappings的value值可能是以下俩种中的一种:
- 已经加载完毕的NamespaceHandler:对应resolve方法的else if分支
- 未加载的NamespaceHandler的类路径:对应resolve方法的else分支
明显,在我们的demo中,获取到的仅仅是UserNamespaceHandler的类路径。
初始完毕handlerMappings之后,返回到resolve方法,此时根据namespaceUri从handleMappings中获取到了自定义的NamespaceHandler的类路径,再通过else分支的代码对自定义NamespaceHandler进行加载(也就是对自定义的UserNamespaceHandler进行了加载),并且调用init方法:
namespaceHandler.init();
而在demo中,自定义的UserNamespaceHandler的init方法如下:
@Override
public void init() {
registerBeanDefinitionParser("user", new UserDefinitionParser());
}
该方法的功能在于注册解析器,在后文findParserForElement方法中,将会通过该注册信息获取到对应的自定义解析器
需要注意的是,registerBeanDefinitionParser方法第一个参数值必须为自定义标签的名称,也就是xsd文件中element的name属性:
<xsd:element name="user">
,否则获取不到解析器
回到parseCustomElement方法,获取到NamespaceHandler之后,调用parse方法进行解析
2.1.2 NamespaceSupport#parse
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 获取到解析器
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
/**
* 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) {
// 获取自定义元素名称,也就是自定义标签的时候重写的init方法里面的elementName的值
String localName = parserContext.getDelegate().getLocalName(element);
// 根据localName获取对应的自定义解析器
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
findParserForElement方法在于通过传入的element获取到2.1.1.1中注册的自定义解析器UserDefinitionParser:
获取UserDefinitionParser
UserDefinitionParser继承了AbstractSingleBeanDefinitionParser,而AbstractSingleBeanDefinitionParser继承了AbstractBeanDefinitionParser,所以核心方法
parser.parse(element, parserContext)
会首先进入AbstractBeanDefinitionParser#parse
2.2 AbstractBeanDefinitionParser#parse
@Override
@Nullable
public final BeanDefinition parse(Element element, ParserContext parserContext) {
// 进行解析,得到definition,此时的definition已经是解析的对象,包含了自定义元素的属性值等
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;
// 是否指定name属性作为别名
if (shouldParseNameAsAliases()) {
String name = element.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(name)) {
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
}
}
// 注册别名
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;
}
首先调用了parseInternal方法
2.3 AbstractSingleBeanDefinitionParser#parseInternal
@Override
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
String parentName = getParentName(element);
if (parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
// 获取自定义的BeanClass,一般情况下是需要重写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.
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();
}
该方法核心点在于:
-
getBeanClass
:指定自定义的bean对象类型 -
doParse
:指定自定义标签解析逻辑
所以,demo中自定义UserDefinitionParser的时候,我们重写了这俩个方法
回过头继续看看parse方法,解析完自定义标签之后,对id、别名等进行了解析,其中resolveId解析逻辑如下:
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
throws BeanDefinitionStoreException {
// 用户指定自动生成id的情况下,直接自动生成id
if (shouldGenerateId()) {
return parserContext.getReaderContext().generateBeanName(definition);
}
else {
// 否则,获取元素定义的id属性
String id = element.getAttribute(ID_ATTRIBUTE);
// 如果没有定义id属性,并且用户指定了缺省id属性情况下自动生成id
if (!StringUtils.hasText(id) && shouldGenerateIdAsFallback()) {
id = parserContext.getReaderContext().generateBeanName(definition);
}
return id;
}
}
其余逻辑较为简单,此处省略
网友评论