在我们进行Spring 框架开发中,估计用到最多的就是bean 标签吧,其实在Spring中像<mvc/><context/>这类标签以及在dubbo配置的标签都是属于自定义的标签,标签的解析,已经由作者就行了解析,我们用就好了,那么我们今天就进行开发一个自己的标签,模拟 <mvc:annotation-driven/>
大家都知道
<mvc:annotation-driven/> 这个标签就是将RequestHandlerMapping Adapter .等类进行了加载注册到了spring 容器中,
为了让大家更好的理解,我们看dubbo jar spring webmvc jar 包下的META-INF 目录:
对就是这三个文件,需要我们进行配置:
1.xsd :xml 约束文件,懂XML的就知道,用来约束我们XML 标签规范的,属性等 2.spring.handlers. :用来解析我们XML的处理器,后面我会给大家说,你一定懂 3.spring.schemas :里面是用来标示xsd 的地址的;
我们看dubbo的spring.handlers 文件:忽略前面的地址,主要是后面的class路径,会被Spring反射调用的
http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
为什么说,它会被反射调用呢?咋们稳扎稳打,我们看Spring源码--在BeanDefinitionParserDelegate 类中,如果不知道,请看一下我的一篇文章-:
link https://www.cnblogs.com/iscys/p/9756458.html
(1),解析自定义的标签元素,也就是说,自定义的标签都会就进入这个方法:
1.
(2),namespaceUrl 就是这个地址,比如解析到你使用了mvc dubbo 标签了,Spring就会找到这个地址,
(3),通过这个地址,我们其实可以大胆的推测出,这个handler 一定是我们在sprin g.handlers中配置的类的实例化,肯定Spring进行了解析,拿到了
后面的类,是不是呢,我们可以点开handler 获得的方法:
http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
点开方法:
1.这个handlerMappings 其实就是 spring.handlers 的一个集合; 2.BeanUtils实例化 3.调用实例化后的init方法
getHandlerMappings 的实现:
(3),我们返回第一步,再执行parse方法,就是我们自己的自定义方法;
------好了,关于Spring 自定义解析其实,Spring 就为我们做了这几件事情
1.实例化一个 自定义handler ,调用init
2.parse 方法调用
OK,我们这么想,Spring为我们的解析将init,方法,parse 方法都写死了,说明Spring 肯定给我们开发者暴露了接口,供我们实现:
1.关于handler的实现:需要
extends NamespaceHandlerSupport 实现 init() 方法;
在init()方法中需要注册我们需要解析的标签内容就像这样;
2.关于new SpringDefinitionParse() 类需要
implements BeanDefinitionParser 实现 其parse 方法,这个是对标签的解析;在Spring中的parse 方法就是间接性的调用这个parse方法;
好了,接下来我们模拟一下mvc 的实现:
1.定义自己的xsd:放入到META-INF目录中:
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns="http://www.example.org/ys" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/ys" elementFormDefault="qualified"> <xsd:complexType name="registryType"> <xsd:attribute name="address" type="xsd:string" use="optional"> <xsd:annotation> <xsd:documentation><![CDATA[ The registry address. ]]></xsd:documentation> </xsd:annotation> </xsd:attribute> <xsd:attribute name="port" type="xsd:string" use="optional"> <xsd:annotation> <xsd:documentation><![CDATA[ The registry default port. ]]></xsd:documentation> </xsd:annotation> </xsd:attribute> <xsd:attribute name="default" type="xsd:string" use="optional"> <xsd:annotation> <xsd:documentation><![CDATA[ Is default. ]]></xsd:documentation> </xsd:annotation> </xsd:attribute> </xsd:complexType> <xsd:element name="mvc" type="registryType"> <xsd:annotation> <xsd:documentation><![CDATA[ The registry config ]]></xsd:documentation> </xsd:annotation> </xsd:element> </xsd:schema>
2.spring.handlers :
http\://www.example.org/ys=com.spring.SpringHandlerSupport
注意这个handlers的key 是
value 就是我们实现类handler,需要自己定义;
3.spring.schemas:
http\://www.example.org/ys.xsd=META-INF/ys.xsd
4.定义自己的handler
public class SpringHandlerSupport extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("mvc", new SpringDefinitionParse()); } }
5.定义自己的解析方法,是需要我们实现的;
public class SpringDefinitionParse implements BeanDefinitionParser { @Override public BeanDefinition parse(Element element, ParserContext parserContext) { //通过element可以得到我们标签定义的内容 String address = element.getAttribute("address"); System.out.println(address); //通过parseContext 我们可以获取到当前的bean 工厂;我们可以通过这个工厂进行注册bean System.out.println(parserContext.getRegistry()); //定义RootBeanDefinition ,设置benclass,这个class对象,我们随便引进来一个类,来测试Spring是不是帮我们进行注册 RootBeanDefinition beanDefinition = new RootBeanDefinition(Test009.class);
//设置bean 名字将bean注册到bean 工厂中 //dubbo 是通过这种方式进行注册的 parserContext.getRegistry().registerBeanDefinition("test009", beanDefinition); // String methodMappingName = parserContext.getReaderContext().registerWithGeneratedName(beanDefinition); // System.out.println(methodMappingName); //spring mvc 通过这种方式注册到bean 工厂中 //parserContext.registerComponent(new BeanComponentDefinition(beanDefinition, methodMappingName)); System.err.println(parserContext.getRegistry()); return beanDefinition; } }
6.配置就完成了:XML我们进行测试:引入我们的schema文件路径
<?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:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" xmlns:ys="http://www.example.org/ys" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd http://www.example.org/ys http://www.example.org/ys.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <ys:mvc address="dd"></ys:mvc> </beans>
7.测试:
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException { ApplicationContext app =new ClassPathXmlApplicationContext("classpath:my.xml"); System.out.println(app.getBean("test009")); }
8.运行结果:
dd//解析的address内容 org.springframework.beans.factory.support.DefaultListableBeanFactory@3dd3bcd: defining beans []; root of factory hierarchy//可以看到bean工厂是 DefaultListableBeanFactory
org.springframework.beans.factory.support.DefaultListableBeanFactory@3dd3bcd: defining beans [test009]; root of factory hierarchy com.java.baseknowledge.sort.Test009@6fd02e5//获得的对象