目录
1、自定义标签的
1.1、PO类、xsd文件
1.2、BeanDefinitionParser 实现类、NamespaceHandlerSupport实现类
1.3、spring.handlers、spring.schemas文件
1.4、测试、测试结果
测试结果 大致猜想一下spring对自定义标签的解析
2、自定义标签原理解析
2.1、parseCustomElement方法
2.2、resolve方法
2.3、parse方法
2.4、BeanDefintionParser的parse()方法
2.5、parseInternal()方法
2.6、自定义标签解析总结
自定义标签的概念我们听起来可能很陌生,但是一提起事务相关的配置
//普通的pojo类
public class User {
private String name;
private Integer age;
}
//xsd描述文件
// schema 中的targetNamespace、xmlns:tns为自定义的命名空间
/**
* 自定义的标签元素的解析处理类
* 通过注册到对应的命名空间上 遇到具体的自定义标签会通过该类进行解析
*/
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class> getBeanClass(Element element) {
return User.class;
}
//具体的标签解析功能的实现
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String name = element.getAttribute("name");
String ageStr = element.getAttribute("age");
if(StringUtils.hasText(name)){
builder.addPropertyValue("name",name);
}
if(StringUtils.hasText(ageStr)){
builder.addPropertyValue("age",Integer.parseInt(ageStr));
}
}
}
/**
* 自定义的命名空间处理器
* 在spring.handles会将命名空间和该类进行绑定 这样使用具体的命名空间来进行标签的解析
* 而命名空间使用的是注册的BeanDefinitionParser来进行具体的解析
*/
public class MyNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
//注册新的解析器 对应user标签 对于命名空间下的所有user标签使用UserBeanDefinitionParser进行解析
registerBeanDefinitionParser("user",new UserBeanDefinitionParser());
}
}
spring容器启动的时候会先加载spring.handlers、spring.schemas文件。
//spring.handlers
//将命名空间和对应的命名处理器做绑定,让spring根据不同的命名空间获取到其对应的命名处理器
//从而进行相应的解析处理
http\://www.example.org/schema/user=com.xiu.customer.MyNamespaceHandler
//spring.schemas
//将对应的命名空间的xsd文件和真实的xsd文件做绑定 用来做自定义标签的校验
//使自定义标签使用者按照规范去使用标签,而非随意的添加或者修改标签的属性
http\://www.example.org/schema/user.xsd=user.xsd
spring xml的配置文件
xmlns:myname="http://www.example.org/schema/user"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.example.org/schema/user http://www.example.org/schema/user.xsd
">
//自定义标签测试
@Test
public void testCustomerTag(){
XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring-mytag.xml"));
com.xiu.customer.User user = beanFactory.getBean("myUser",com.xiu.customer.User.class);
System.out.println("获取到自定义标签解析出来的用户,用户姓名:"+user.getName()+", 年龄: "+user.getAge());
}
spring容器启动的时候会先加载spring.sechmas和spring.handlers文件。这两个文件前者定义了xsd的规范信息,让我得以按照xsd的规范进行自定义标签的编写。后者定义了命名空间对应的解析处理器,通过对应的处理器获得到对应的BeanDefinitionParser 对标签元素进行解析,并转换为BeanDefinition对象。
上文我们分析了默认标签和自定义标签,现在分析自定义标签解析 delegate.parseCustomElement(ele);
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
//根据元素标签获取对应的命名空间 例子中命名空间为http\://www.example.org/schema/user
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
//根据命名空间获取对应的NamespaceHandler
//这里会根据spring.handlers来获取到对应命名空间的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的parse进行标签元素解析
return handler.parse(ele, new ParserContext(this.readerContext,
this, containingBd));
}
客户自定义解析标签流程:
public NamespaceHandler resolve(String namespaceUri) {
//获取命名空间和NamespaceHandler缓存 缓存中NamespaceHandler 可能是对应的class字符串
//也可能是对应的NamespaceHandler实例
//getHandlerMappings()方法也是从缓存中获取 如果缓存中没有则加载属性文件获取
Map handlerMappings = getHandlerMappings();
//从缓存中获取NamspaceHandler 如果空返回空
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
//是NamespaceHandler实例返回实例
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
//不是实例获取class 去实例化NamespaceHandler对象
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的init()方法
//我们自定义的MyNamespaceHandler 的init()方法注册一个UserBeanDefinitionParser
//来解析我们编写的user.xsd对应的标签,这样spring会根据映射关系找到
//UserBeanDefinitionParser去解析自定义标签
NamespaceHandler namespaceHandler = (NamespaceHandler)
BeanUtils.instantiateClass(handlerClass);
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);
}
}
}
public BeanDefinition parse(Element element, ParserContext parserContext) {
//根据element元素 从parserContext中获取对应的BeanDefinitionParser
//对于我们自定义标签解析使用的是UserBeanDefinitionParser实例
BeanDefinitionParser parser = this.findParserForElement(element, parserContext);
//UserBeanDefinitionParser 调用其parse()进行元素标签的解析
return parser != null ? parser.parse(element, parserContext) : null;
}
//获取对应的BeanDefinitionParser根据element的localName获取对应的BeanDefinitionParser
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
BeanDefinitionParser parser = (BeanDefinitionParser)this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionParser
for element [" + localName + "]", element);
}
return parser;
}
上面的步骤获取到了对应的BeanDefinitionParser,接下来调用的其对应的parser(),首先是调用AbstractBeanDefinition类的
parser方法。
//调用BeanDefintion的parser()方法
public final BeanDefinition parse(Element element, ParserContext parserContext) {
//调用其parseInternal()方法 进行element到BeanDefintion对象的转换
//该方法也是对自定义标签的解析
AbstractBeanDefinition definition = this.parseInternal(element, parserContext);
if (definition != null && !parserContext.isNested()) {
try {
//获取自定义标签的id
String id = this.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 (this.shouldParseNameAsAliases()) {
String name = element.getAttribute("name");
if (StringUtils.hasLength(name)) {
aliases = StringUtils.trimArrayElements
(StringUtils.commaDelimitedListToStringArray(name));
}
}
//创建BeanDefintionHolder对象
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
//注册对应的Bendefintion是对象
this.registerBeanDefinition(holder, parserContext.getRegistry());
if (this.shouldFireEvents()) {
BeanComponentDefinition componentDefinition =
new BeanComponentDefinition(holder);
this.postProcessComponentDefinition(componentDefinition);
parserContext.registerComponent(componentDefinition);
}
} catch (BeanDefinitionStoreException var8) {
String msg = var8.getMessage();
parserContext.getReaderContext().error(msg != null ?
msg : var8.toString(), element);
return null;
}
}
return definition;
}
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
//创建BeanDefinitionBuilder对象
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
//解析自定义的标签的parentName,beanClass。beanClassName等属性 设置进入
// BeanDefinitionBuilder 对象中
String parentName = this.getParentName(element);
if (parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
Class> beanClass = this.getBeanClass(element);
if (beanClass != null) {
builder.getRawBeanDefinition().setBeanClass(beanClass);
} else {
String beanClassName = this.getBeanClassName(element);
if (beanClassName != null) {
builder.getRawBeanDefinition().setBeanClassName(beanClassName);
}
}
builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
if (containingBd != null) {
builder.setScope(containingBd.getScope());
}
if (parserContext.isDefaultLazyInit()) {
builder.setLazyInit(true);
}
//调用其doParse() 到这里和我们对应的注册器UserBeanDefintionParser实现的
//doParse()方法印证上了。
this.doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}
自定义标签解析和我们的猜想一致。