先进性一个简单的自定义标签的例子
首先在META-INF下创建文件spring.handlers(名字和路径固定)在其中添加
http\://www.test.com/schema/tags/urlMap=com.test.common.security.sdk.spring.handler.UrlMapNamespaceSupport |
这一步是为了向spring注册对应的转换解析器。有可能你同一个uri下面定义多个tag。你可以用同一个tag进行解析,也可以用不同的tag进行解析。
UrlMapNamespaceSupport代码如下
public class UrlMapNamespaceSupport extends NamespaceHandlerSupport { @Override public void init() { //定义解析器,告诉spring,遇见urlMap标签,交由UrlMapBeanDefinitionParse //进行解析,并且把对应的关系放到parsers Map中 registerBeanDefinitionParser("urlMap", new UrlMapBeanDefinitionParse()); registerBeanDefinitionParser("clientId", new ClientIdBeanDefinitionParse()); registerBeanDefinitionParser("url", new URLBeanDefinitionParse()); registerBeanDefinitionParser("relation", new RouteRelationDefinitionParse()); }
} |
下面是对应的转换解析器
public class UrlMapBeanDefinitionParse extends AbstractSimpleBeanDefinitionParser {
@Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
builder.addPropertyValue("urlMap", parseMapElement(element, parserContext, builder));
}
private Map parseMapElement(Element mapEle,ParserContext parserContext, BeanDefinitionBuilder builder){ List entryEles = DomUtils.getChildElementsByTagName(mapEle, "clientId"); ManagedMap clientIdMap = new ManagedMap(entryEles.size()); clientIdMap.setMergeEnabled(true); clientIdMap.setSource(parserContext.getReaderContext().extractSource(mapEle)); for (Iterator it = entryEles.iterator(); it.hasNext();) { Element entryEle = (Element) it.next();
String mapKey = entryEle.getAttribute("pattern"); /** * 继续进行标签解析,将其解析成对应的url实体。然后加入到对应的数据中 */ BeanDefinition definition = parserContext.getDelegate().parseCustomElement( entryEle, builder.getRawBeanDefinition()); clientIdMap.put(mapKey, definition); } return clientIdMap;
}
@Override protected Class<?> getBeanClass(Element element) { return ClientUrlMapBean.class; }
} |
/** * 解析其中的clientId标签 * <clientId pattern="clientId*" exclusion=""> <url pattern="" ref=""/> <url pattern="" ref=""/> * </clientId> * @author Administrator * */ public class ClientIdBeanDefinitionParse extends AbstractSimpleBeanDefinitionParser{
@Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { String excusion = element.getAttribute("exclusion"); builder.addPropertyValue("exclusionClientId", excusion); builder.addPropertyValue("urlList", parseListElement(element, parserContext, builder));
}
private List parseListElement(Element mapEle,ParserContext parserContext, BeanDefinitionBuilder builder){ List entryEles = DomUtils.getChildElementsByTagName(mapEle, "url"); ManagedList urlList = new ManagedList(entryEles.size()); urlList.setMergeEnabled(true); urlList.setSource(parserContext.getReaderContext().extractSource(mapEle)); for (Iterator it = entryEles.iterator(); it.hasNext();) { Element entryEle = (Element) it.next(); /** * 继续进行标签解析,将其解析成对应的url实体。然后加入到对应的数据中 */ BeanDefinition definition = parserContext.getDelegate().parseCustomElement( entryEle, builder.getRawBeanDefinition()); urlList.add(definition); } return urlList;
}
@Override protected Class<?> getBeanClass(Element element) { // TODO Auto-generated method stub return ClientMapExtendBean.class; }
} |
/** * * 解析url对应的数据 * @author Administrator * */ public class URLBeanDefinitionParse extends AbstractSimpleBeanDefinitionParser {
private Logger LOGGER = LoggerFactory.getLogger(URLBeanDefinitionParse.class);
@Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { //super.doParse(element, parserContext, builder); String pattern = element.getAttribute("pattern"); String ref = element.getAttribute("ref"); String condition = element.getAttribute("condition"); String paramMap = element.getAttribute("paramFilter"); builder.addPropertyValue("url", pattern); builder.addPropertyReference("firstVerification", ref); builder.addPropertyReference("condition", condition); builder.addPropertyReference("paramMap", paramMap); }
@Override protected Class<?> getBeanClass(Element element) { return UrlFilterMapBean.class; }
} |
/** * <relation ref="当前策略" pass="成功运行策略" fail="失败运行策略"/> * 解析该标签 ref 对应的是策略类 * pass 对应的是当前策略验证通过继续校验的策略 * fail 对应的是当前策略执行失败继续校验的策略 */ public class RouteRelationDefinitionParse extends AbstractSimpleBeanDefinitionParser {
@Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { String ref = element.getAttribute("verification"); String pass = element.getAttribute("pass"); String fail = element.getAttribute("fail"); builder.addPropertyReference("currentVerification",ref); builder.addPropertyReference("passNext",pass); builder.addPropertyReference("failNext",fail); } @Override protected Class<?> getBeanClass(Element element) { return RouteRelation.class; }
} |
然后在META-INF文件下创建一个url-map.xsd文件,文件内容如下
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns="http://www.hsyuntai.com/schema/tags/urlMap" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:tool="http://www.springframework.org/schema/tool" targetNamespace="http://www.test/schema/tags/urlMap" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans" schemaLocation="http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"/> <xsd:import namespace="http://www.springframework.org/schema/tool" schemaLocation="http://www.springframework.org/schema/tool/spring-tool-4.3.xsd"/>
<xsd:element name="url"> <xsd:annotation> <xsd:documentation><![CDATA[ 创建一个UrlFilterMapBean 对象,该对象封装了 url 以及要进行的策略 ]]></xsd:documentation> </xsd:annotation> <xsd:complexType> <xsd:attribute name="pattern" type="xsd:string"> <xsd:annotation> <xsd:documentation><![CDATA[ 当前的url,可以进行正则表达式,如果是匹配,请注意是** ]]></xsd:documentation> </xsd:annotation> </xsd:attribute> <xsd:attribute name="ref" type="xsd:string"> <xsd:annotation> <xsd:documentation><![CDATA[ 策略入口类。ISecurityVerification引用 ]]></xsd:documentation> <xsd:appinfo> <tool:annotation kind="ref"/> <tool:expected-type type="com.yuntai.common.security.sdk.verification.base.ISecurityVerificationValidate"/> </xsd:appinfo> </xsd:annotation> </xsd:attribute> <xsd:attribute name="condition" type="xsd:string"> <xsd:annotation> <xsd:documentation><![CDATA[ 触发条件,该条件没有,如果是url全部触发。 ]]></xsd:documentation> <xsd:appinfo> <tool:annotation kind="ref"/> <tool:expected-type type="com.yuntai.common.security.sdk.verification.intefacer.VerificationTriggerCondition"/> </xsd:appinfo> </xsd:annotation> </xsd:attribute> <xsd:attribute name="paramFilter" type="xsd:string"> <xsd:annotation> <xsd:documentation><![CDATA[ 判断当前token的状态参数列表 ]]></xsd:documentation> <xsd:appinfo> <tool:annotation kind="ref"/> <tool:expected-type type="com.yuntai.common.security.sdk.verification.intefacer.VerificationExtendParam"/> </xsd:appinfo> </xsd:annotation> </xsd:attribute>
</xsd:complexType> </xsd:element>
<xsd:element name="clientId"> <xsd:annotation> <xsd:documentation><![CDATA[ 定义 ClientMapExtendBean 该类中有 一个url列表和 对应的exclusion属性 ]]></xsd:documentation> </xsd:annotation> <xsd:complexType> <xsd:complexContent> <!-- extendsion相当于继承 --> <xsd:extension base="beans:identifiedType"> <xsd:sequence> <xsd:element minOccurs="1" maxOccurs="1000" ref="url"/> </xsd:sequence> <xsd:attribute name="exclusion" type="xsd:string"> <xsd:annotation> <xsd:documentation><![CDATA[ 需要排除的clientId,多个需要用,分割 ]]></xsd:documentation> </xsd:annotation> </xsd:attribute> <xsd:attribute name="pattern" type="xsd:string"> <xsd:annotation> <xsd:documentation><![CDATA[ 需要进行过滤的url表达式。**代表任意 ]]></xsd:documentation> </xsd:annotation> </xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType> </xsd:element>
<xsd:element name="urlMap"> <xsd:annotation> <xsd:documentation><![CDATA[ 创建一个ClientUrlMapBean,该类最终返回的是一个Map<String,ClientMapExtendBean> ]]></xsd:documentation> </xsd:annotation> <xsd:complexType> <xsd:complexContent> <xsd:extension base="beans:identifiedType"> <xsd:sequence> <xsd:element ref="clientId" minOccurs="1" maxOccurs="1000"/> </xsd:sequence> </xsd:extension> </xsd:complexContent> </xsd:complexType> </xsd:element> </xsd:schema> |
在META-INFO下建立一个spring.schemas文件,用来存放uri和本地xsd的对应关系,为了让spring容器能够解析到对应的xsd文件。如果你是基于远程的。可以不定义映射关系
http\://www.test.com/schema/tags/urlMap.xsd=META-INF/url-map.xsd |
最后在spring.xml中进行引入
<?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:test="http://www.test.com/schema/tags/urlMap" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.test.com/schema/tags/urlMap http://www.test.com/schema/tags/urlMap.xsd"> <test:relation id="account" verification="andGroupStragy" fail="faceValidate"></test:relation> <test:relation id="faceValidate" verification="face" fail="timeVerification" ></test:relation> <test:relation id="faceValidateFinish" verification="andGroupStragy" fail="face"></test:relation> <test:urlMap id="urlMap1"> <test:clientId pattern="**"> <test:url pattern="/1112/1112" condition="" paramFilter="" ref="account"/> <test:url pattern="/1112/1113" ref="faceValidateFinish"/> </test:clientId> </test:urlMap> </beans> |
下面就对着源码进行分析。Spring是如何解析自定义标签的
首先进入BeanDefinitionParserDelegate的parseCustomElement
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { //获取标签的命名空间的uri String namespaceUri = getNamespaceURI(ele); //根据命名空间查找对应的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; } //调用对应的hanlder 进行解析里边的具体标签 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); } |
下面具体看一下spring是如何查找命名空间和handler的对应关系的,进入DefaultNamespaceHandlerResolver的resolve方法
public NamespaceHandler resolve(String namespaceUri) { //获取所有的handlerMapping.就是解析META-INF/spring.handlers下的properties文//件,还记的demo中的我们在里边加的文件吗 Map<String, Object> handlerMappings = getHandlerMappings(); Object handlerOrClassName = handlerMappings.get(namespaceUri); //查看那有没有获取对应的handler 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("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", ex); } catch (LinkageError err) { throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", err); } } } |
下面我们看一下parseCustomElement函数中的handler.parse(ele, new ParserContext(this.readerContext, this, containingBd))得过程
进入NamespaceHandlerSupport的findParserForElement
@Override public BeanDefinition parse(Element element, ParserContext parserContext) { //首先根据当前的elment找到对应的parser //就是从parse map中根据elment的test:urlMap 查找对应的parase //调用其parse进行解析。这个就会调用我们的复写的具体类。 //会通过用户自己构建beanDefinition.然后调用注册到beanFactory。由于此时的parserContext包含xmlReaderContext,而xmlReaderContext又包含了beanFactory对象。所以可以获取到。 return findParserForElement(element, parserContext).parse(element, parserContext); }
/** * Locates the {@link BeanDefinitionParser} from the register implementations using * the local name of the supplied {@link Element}. */ 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; } |