目录
2)、获取自定义的解析器(AbstractSingleBeanDefinitionParser的子类)
为什么要看自定义标签,因为Spring中aop(aspectj-autoproxy)、事务开关(tx)等很多都实现了自定义标签,并且在当前项目中搜索了一下spring.handlers,发现有至少50个实现了自定义标签。
1、自定义标签实现
1)、定义Bean
@Data
public class KevinInfo {
private String name;
}
在maven项目resources(或者打包后的classes)下添加META-INF目录
2)、spring.handlers
添加文件的内容为:
http\://www.kevin.com/schema/kevinInfo=com.kevin.tool.spring.ioc.customlabel.KevinNamespaceHandler
定义NamespaceHandlerSupport的子类并在init方法中调用registerBeanDefinitionParser方法,如下:
public class KevinNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
// kevinInfo对应 Xsd文件中的 <element name="kevinInfo">
registerBeanDefinitionParser("kevinInfo", new KevinInfoParser());
}
}
同时也指定了解析器,实现自AbstractSingleBeanDefinitionParser
public class KevinInfoParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return KevinInfo.class;
}
@Override
protected void doParse(Element element, ParserContext parserContext,
BeanDefinitionBuilder builder) {
String name = element.getAttribute("name");
builder.addPropertyValue("name", name);
}
}
3)、spring.schemas
添加文件的内容为:
http\://www.kevin.com/schema/kevin.xsd=META-INF/kevin.xsd
指定了xsd的位置,为同级目录,如下:
<?xml version="1.0" encoding="UTF-8" ?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.kevin.com/schema/kevinInfo"
elementFormDefault="qualified">
<element name="kevinInfo">
<complexType>
<!-- bean id -->
<attribute name="id" type="string" />
<attribute name="name" type="string" />
</complexType>
</element>
</schema>
3)、Spring Xml配置文件
4) 、测试
public class CustomLabelTest {
public static void main(String[] args) {
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring-bean.xml"));
KevinInfo kevinInfo = (KevinInfo)beanFactory.getBean("beanId");
System.out.println(kevinInfo.getName());
}
}
2、自定义标签实现分析
回到上一篇分析自定义标签的位置,获取到了非Spring自定义的标签
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
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;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
继续分析自定义标签:
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
之前有分析过delefate是外层初始化传入,并且在调用方法之前有创建NamespaceResolver:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
调用registerBeanDefinitions时调用了createReaderContext方法,内部又初始化了NamespaceResolver为DefaultNamespaceHandlerResolver
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
ClassLoader cl = (getResourceLoader() != null ?
getResourceLoader().getClassLoader() : getBeanClassLoader());
return new DefaultNamespaceHandlerResolver(cl);
}
进入构造函数:
public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) {
this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}
继续,(找了好久不知道DefaultNamespaceHandlerResolver内部的handlerMappings不知道是什么时候初始化的),还好debug进行调试,神奇的一幕发生了。
这一步,本来是抛异常的,并且只都为空。debug,下一步
异常没有了,handlerMappings初始化完了。网上有的说是其他线程完成的。不知道了,反正在这里初始化完了。
还是继续往下:
1)、获取NamespaceHandler
所以当前拿到的命名空间就是http://www.kevin.com/schema/kevinInfo,去XmlReaderContext的namespaceHandlerResolver中handlerMappings去获取。调用resolve方法:
public NamespaceHandler resolve(String namespaceUri) {
Map<String, Object> handlerMappings = getHandlerMappings();
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.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);
}
}
}
使用反射获取到当前的NamespaceHandler 为KevinNamespaceHandler,调用其init方法,就是我们之前定义的:
@Override
public void init() {
registerBeanDefinitionParser("kevinInfo", new KevinInfoParser());
}
protected final void registerBeanDefinitionParser(String elementName,
BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}
这才将key和value值注入到NamespaceHandlerSupport中的parsers中,再调用parse方法。
继续handler.parse(ele, new ParserContext(this.readerContext, this, containingBd))
2)、获取自定义的解析器(AbstractSingleBeanDefinitionParser的子类)
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
很清楚的,刚才put进去,获取到了自定义的KevinInfoParser,再调用自定义的parse方法。
@Override
protected void doParse(Element element, ParserContext parserContext,
BeanDefinitionBuilder builder) {
String name = element.getAttribute("name");
builder.addPropertyValue("name", name);
}
这样就实现了自定义标签的实现和解析,over!