4_自定义标签的解析

先进性一个简单的自定义标签的例子

首先在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;

         }

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值